def __init__(self, unspent_store: CoinStore, override_constants: Dict = {}): # Allow passing in custom overrides self.constants: Dict = consensus_constants.copy() for key, value in override_constants.items(): self.constants[key] = value # Transactions that were unable to enter mempool, used for retry. (they were invalid) self.potential_txs: Dict[bytes32, SpendBundle] = {} # Keep track of seen spend_bundles self.seen_bundle_hashes: Dict[bytes32, bytes32] = {} # Mempool for each tip self.mempools: Dict[bytes32, Mempool] = {} # old_mempools will contain transactions that were removed in the last 10 blocks self.old_mempools: SortedDict[uint32, Dict[bytes32, MempoolItem]] = SortedDict() self.unspent_store = unspent_store tx_per_sec = self.constants["TX_PER_SEC"] sec_per_block = self.constants["BLOCK_TIME_TARGET"] block_buffer_count = self.constants["MEMPOOL_BLOCK_BUFFER"] # MEMPOOL_SIZE = 60000 self.mempool_size = tx_per_sec * sec_per_block * block_buffer_count self.potential_cache_size = 300 self.seen_cache_size = 10000 self.coinbase_freeze = self.constants["COINBASE_FREEZE_PERIOD"]
def create_genesis_block( self, input_constants: Dict, challenge_hash=bytes([0] * 32), seed: bytes = b"", reward_puzzlehash: Optional[bytes32] = None, ) -> FullBlock: """ Creates the genesis block with the specified details. """ test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value return self._create_block( test_constants, challenge_hash, uint32(0), bytes([0] * 32), uint64(0), uint128(0), uint64(int(time.time())), uint64(test_constants["DIFFICULTY_STARTING"]), uint64(test_constants["MIN_ITERS_STARTING"]), seed, True, reward_puzzlehash, )
def __init__(self, farmer_config: Dict, key_config: Dict, override_constants={}): self.config = farmer_config self.key_config = key_config self.harvester_responses_header_hash: Dict[bytes32, bytes32] = {} self.harvester_responses_challenge: Dict[bytes32, bytes32] = {} self.harvester_responses_proofs: Dict[bytes32, ProofOfSpace] = {} self.harvester_responses_proof_hash_to_qual: Dict[bytes32, bytes32] = {} self.challenges: Dict[ uint128, List[farmer_protocol.ProofOfSpaceFinalized]] = {} self.challenge_to_weight: Dict[bytes32, uint128] = {} self.challenge_to_height: Dict[bytes32, uint32] = {} self.challenge_to_best_iters: Dict[bytes32, uint64] = {} self.seen_challenges: Set[bytes32] = set() self.unfinished_challenges: Dict[uint128, List[bytes32]] = {} self.current_weight: uint128 = uint128(0) self.coinbase_rewards: Dict[uint32, Any] = {} self.proof_of_time_estimate_ips: uint64 = uint64(10000) self.constants = consensus_constants.copy() self.server = None self._shut_down = False for key, value in override_constants.items(): self.constants[key] = value
async def create( coin_store: CoinStore, block_store: BlockStore, override_constants: Dict = {}, ): """ Initializes a blockchain with the header blocks from disk, assuming they have all been validated. Uses the genesis block given in override_constants, or as a fallback, in the consensus constants config. """ self = Blockchain() self.lock = asyncio.Lock() # External lock handled by full node cpu_count = multiprocessing.cpu_count() self.pool = concurrent.futures.ProcessPoolExecutor( max_workers=max(cpu_count - 1, 1)) self.constants = consensus_constants.copy() for key, value in override_constants.items(): self.constants[key] = value self.tips = [] self.height_to_hash = {} self.headers = {} self.coin_store = coin_store self.block_store = block_store self._shut_down = False self.genesis = FullBlock.from_bytes(self.constants["GENESIS_BLOCK"]) self.coinbase_freeze = self.constants["COINBASE_FREEZE_PERIOD"] await self._load_chain_from_store() return self
def create_next_block( self, input_constants: Dict, prev_block: FullBlock, timestamp: uint64, difficulty: uint64, ips: uint64, seed: bytes = b"", ) -> FullBlock: """ Creates the next block with the specified details. """ test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value assert prev_block.header_block.challenge return self._create_block( test_constants, prev_block.header_block.challenge.get_hash(), uint32(prev_block.height + 1), prev_block.header_hash, prev_block.header_block.challenge.total_iters, prev_block.weight, timestamp, uint64(difficulty), ips, seed, )
def create_next_block( self, input_constants: Dict, prev_block: FullBlock, timestamp: uint64, update_difficulty: bool, difficulty: uint64, min_iters: uint64, seed: bytes = b"", reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: BLSSignature = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates the next block with the specified details. """ test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value assert prev_block.proof_of_time is not None if update_difficulty: challenge = Challenge( prev_block.proof_of_space.challenge_hash, std_hash( prev_block.proof_of_space.get_hash() + prev_block.proof_of_time.output.get_hash() ), difficulty, ) else: challenge = Challenge( prev_block.proof_of_space.challenge_hash, std_hash( prev_block.proof_of_space.get_hash() + prev_block.proof_of_time.output.get_hash() ), None, ) return self._create_block( test_constants, challenge.get_hash(), uint32(prev_block.height + 1), prev_block.header_hash, prev_block.header.data.total_iters, prev_block.weight, timestamp, uint64(difficulty), min_iters, seed, False, reward_puzzlehash, transactions, aggsig, fees, )
def __init__( self, farmer_config: Dict, pool_config: Dict, keychain: Keychain, override_constants={}, ): self.config = farmer_config self.harvester_responses_header_hash: Dict[bytes32, bytes32] = {} self.harvester_responses_challenge: Dict[bytes32, bytes32] = {} self.harvester_responses_proofs: Dict[bytes32, ProofOfSpace] = {} self.harvester_responses_proof_hash_to_qual: Dict[bytes32, bytes32] = {} self.challenges: Dict[ uint128, List[farmer_protocol.ProofOfSpaceFinalized]] = {} self.challenge_to_weight: Dict[bytes32, uint128] = {} self.challenge_to_height: Dict[bytes32, uint32] = {} self.challenge_to_best_iters: Dict[bytes32, uint64] = {} self.challenge_to_estimates: Dict[bytes32, List[float]] = {} self.seen_challenges: Set[bytes32] = set() self.unfinished_challenges: Dict[uint128, List[bytes32]] = {} self.current_weight: uint128 = uint128(0) self.proof_of_time_estimate_ips: uint64 = uint64(10000) self.constants = consensus_constants.copy() self._shut_down = False self.server = None self.keychain = keychain self.state_changed_callback: Optional[Callable] = None # This is the farmer configuration self.wallet_target = bytes.fromhex( self.config["xch_target_puzzle_hash"]) self.pool_public_keys = [ PublicKey.from_bytes(bytes.fromhex(pk)) for pk in self.config["pool_public_keys"] ] # This is the pool configuration, which should be moved out to the pool once it exists self.pool_target = bytes.fromhex(pool_config["xch_target_puzzle_hash"]) self.pool_sks = [ sk.get_private_key() for (sk, _) in self.keychain.get_all_private_keys() ] self.pool_sks_map: Dict = {} for key in self.pool_sks: self.pool_sks_map[bytes(key.get_public_key())] = key assert len(self.wallet_target) == 32 assert len(self.pool_target) == 32 if len(self.pool_sks) == 0: error_str = "No keys exist. Please run 'chia keys generate' or open the UI." raise RuntimeError(error_str) for key, value in override_constants.items(): self.constants[key] = value
async def create( config: Dict, private_key: ExtendedPrivateKey, root_path: Path, name: str = None, override_constants: Dict = {}, local_test: bool = False, ): self = WalletNode() self.config = config self.constants = consensus_constants.copy() self.root_path = root_path self.local_test = local_test for key, value in override_constants.items(): self.constants[key] = value if name: self.log = logging.getLogger(name) else: self.log = logging.getLogger(__name__) db_path_key_suffix = str( private_key.get_public_key().get_fingerprint()) path = path_from_root( self.root_path, f"{config['database_path']}-{db_path_key_suffix}") mkdir(path.parent) self.wallet_state_manager = await WalletStateManager.create( private_key, config, path, self.constants) self.wallet_state_manager.set_pending_callback( self._pending_tx_handler) # Normal operation data self.cached_blocks = {} self.future_block_hashes = {} # Sync data self._shut_down = False self.proof_hashes = [] self.header_hashes = [] self.header_hashes_error = False self.short_sync_threshold = 15 self.potential_blocks_received = {} self.potential_header_hashes = {} self.server = None self.tasks = [] return self
def __init__(self, root_path: Path, override_constants={}): self.root_path = root_path # From filename to prover self.provers = {} self.failed_to_open_filenames = set() self.no_key_filenames = set() self._is_shutdown = False self.global_connections: Optional[PeerConnections] = None self.farmer_public_keys = [] self.pool_public_keys = [] self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=10) self.state_changed_callback = None self.server = None self.constants = consensus_constants.copy() self.cached_challenges = [] for key, value in override_constants.items(): self.constants[key] = value
async def create( config: Dict, key_config: Dict, name: str = None, override_constants: Dict = {}, ): self = WalletNode() self.config = config self.key_config = key_config self.constants = consensus_constants.copy() for key, value in override_constants.items(): self.constants[key] = value if name: self.log = logging.getLogger(name) else: self.log = logging.getLogger(__name__) path = path_from_root(DEFAULT_ROOT_PATH, config["database_path"]) mkdir(path.parent) self.wallet_state_manager = await WalletStateManager.create( key_config, config, path, self.constants) self.wallet_state_manager.set_pending_callback( self._pending_tx_handler) # Normal operation data self.cached_blocks = {} self.future_block_hashes = {} # Sync data self._shut_down = False self.proof_hashes = [] self.header_hashes = [] self.header_hashes_error = False self.short_sync_threshold = 15 self.potential_blocks_received = {} self.potential_header_hashes = {} self.server = None return self
def create_genesis_block(self, input_constants: Dict, challenge_hash=bytes([0] * 32), seed: bytes = b"") -> FullBlock: """ Creates the genesis block with the specified details. """ test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value return self._create_block( test_constants, challenge_hash, uint32(0), bytes([0] * 32), uint64(0), uint64(0), uint64(int(time.time())), uint64(test_constants["DIFFICULTY_STARTING"]), uint64(test_constants["VDF_IPS_STARTING"]), seed, )
import asyncio import pytest import time from typing import Dict, Any from tests.setup_nodes import setup_full_system from tests.block_tools import BlockTools from src.consensus.constants import constants as consensus_constants bt = BlockTools() test_constants: Dict[str, Any] = consensus_constants.copy() test_constants.update({ "DIFFICULTY_STARTING": 500, "MIN_ITERS_STARTING": 2**15 }) test_constants["GENESIS_BLOCK"] = bytes( bt.create_genesis_block(test_constants, bytes([0] * 32), b"0")) @pytest.fixture(scope="module") def event_loop(): loop = asyncio.get_event_loop() yield loop class TestSimulation: @pytest.fixture(scope="function") async def simulation(self): async for _ in setup_full_system(test_constants): yield _
def get_consecutive_blocks( self, input_constants: Dict, num_blocks: int, block_list: List[FullBlock] = [], seconds_per_block=None, seed: bytes = b"", reward_puzzlehash: bytes32 = None, transaction_data_at_height: Dict[int, Tuple[Program, BLSSignature]] = None, fees: uint64 = uint64(0), ) -> List[FullBlock]: if transaction_data_at_height is None: transaction_data_at_height = {} test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value if seconds_per_block is None: seconds_per_block = test_constants["BLOCK_TIME_TARGET"] if len(block_list) == 0: if "GENESIS_BLOCK" in test_constants: block_list.append(FullBlock.from_bytes(test_constants["GENESIS_BLOCK"])) else: block_list.append( self.create_genesis_block(test_constants, std_hash(seed), seed) ) prev_difficulty = test_constants["DIFFICULTY_STARTING"] curr_difficulty = prev_difficulty curr_min_iters = test_constants["MIN_ITERS_STARTING"] elif len(block_list) < ( test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"] ): # First epoch (+delay), so just get first difficulty prev_difficulty = block_list[0].weight curr_difficulty = block_list[0].weight assert test_constants["DIFFICULTY_STARTING"] == prev_difficulty curr_min_iters = test_constants["MIN_ITERS_STARTING"] else: curr_difficulty = block_list[-1].weight - block_list[-2].weight prev_difficulty = ( block_list[-1 - test_constants["DIFFICULTY_EPOCH"]].weight - block_list[-2 - test_constants["DIFFICULTY_EPOCH"]].weight ) assert block_list[-1].proof_of_time is not None curr_min_iters = calculate_min_iters_from_iterations( block_list[-1].proof_of_space, curr_difficulty, block_list[-1].proof_of_time.number_of_iterations, ) starting_height = block_list[-1].height + 1 timestamp = block_list[-1].header.data.timestamp for next_height in range(starting_height, starting_height + num_blocks): if ( next_height > test_constants["DIFFICULTY_EPOCH"] and next_height % test_constants["DIFFICULTY_EPOCH"] == test_constants["DIFFICULTY_DELAY"] ): # Calculates new difficulty height1 = uint64( next_height - ( test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"] ) - 1 ) height2 = uint64(next_height - (test_constants["DIFFICULTY_EPOCH"]) - 1) height3 = uint64(next_height - (test_constants["DIFFICULTY_DELAY"]) - 1) if height1 >= 0: block1 = block_list[height1] iters1 = block1.header.data.total_iters timestamp1 = block1.header.data.timestamp else: block1 = block_list[0] timestamp1 = ( block1.header.data.timestamp - test_constants["BLOCK_TIME_TARGET"] ) iters1 = uint64(0) timestamp2 = block_list[height2].header.data.timestamp timestamp3 = block_list[height3].header.data.timestamp block3 = block_list[height3] iters3 = block3.header.data.total_iters term1 = ( test_constants["DIFFICULTY_DELAY"] * prev_difficulty * (timestamp3 - timestamp2) * test_constants["BLOCK_TIME_TARGET"] ) term2 = ( (test_constants["DIFFICULTY_WARP_FACTOR"] - 1) * ( test_constants["DIFFICULTY_EPOCH"] - test_constants["DIFFICULTY_DELAY"] ) * curr_difficulty * (timestamp2 - timestamp1) * test_constants["BLOCK_TIME_TARGET"] ) # Round down after the division new_difficulty_precise: uint64 = uint64( (term1 + term2) // ( test_constants["DIFFICULTY_WARP_FACTOR"] * (timestamp3 - timestamp2) * (timestamp2 - timestamp1) ) ) new_difficulty = uint64( truncate_to_significant_bits( new_difficulty_precise, test_constants["SIGNIFICANT_BITS"] ) ) max_diff = uint64( truncate_to_significant_bits( test_constants["DIFFICULTY_FACTOR"] * curr_difficulty, test_constants["SIGNIFICANT_BITS"], ) ) min_diff = uint64( truncate_to_significant_bits( curr_difficulty // test_constants["DIFFICULTY_FACTOR"], test_constants["SIGNIFICANT_BITS"], ) ) if new_difficulty >= curr_difficulty: new_difficulty = min(new_difficulty, max_diff,) else: new_difficulty = max([uint64(1), new_difficulty, min_diff]) min_iters_precise = uint64( (iters3 - iters1) // ( test_constants["DIFFICULTY_EPOCH"] * test_constants["MIN_ITERS_PROPORTION"] ) ) curr_min_iters = uint64( truncate_to_significant_bits( min_iters_precise, test_constants["SIGNIFICANT_BITS"] ) ) prev_difficulty = curr_difficulty curr_difficulty = new_difficulty time_taken = seconds_per_block timestamp += time_taken transactions: Optional[Program] = None aggsig: Optional[BLSSignature] = None if next_height in transaction_data_at_height: transactions, aggsig = transaction_data_at_height[next_height] update_difficulty = ( next_height % test_constants["DIFFICULTY_EPOCH"] == test_constants["DIFFICULTY_DELAY"] ) block_list.append( self.create_next_block( test_constants, block_list[-1], timestamp, update_difficulty, curr_difficulty, curr_min_iters, seed, reward_puzzlehash, transactions, aggsig, fees, ) ) return block_list
def get_consecutive_blocks( self, input_constants: Dict, num_blocks: int, block_list: List[FullBlock] = [], seconds_per_block=constants["BLOCK_TIME_TARGET"], seed: bytes = b"", ) -> List[FullBlock]: test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value if len(block_list) == 0: if "GENESIS_BLOCK" in test_constants: block_list.append( FullBlock.from_bytes(test_constants["GENESIS_BLOCK"])) else: block_list.append( self.create_genesis_block(test_constants, sha256(seed).digest(), seed)) prev_difficulty = test_constants["DIFFICULTY_STARTING"] curr_difficulty = prev_difficulty curr_ips = test_constants["VDF_IPS_STARTING"] elif len(block_list) < (test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"]): # First epoch (+delay), so just get first difficulty prev_difficulty = block_list[0].weight curr_difficulty = block_list[0].weight assert test_constants["DIFFICULTY_STARTING"] == prev_difficulty curr_ips = test_constants["VDF_IPS_STARTING"] else: curr_difficulty = block_list[-1].weight - block_list[-2].weight prev_difficulty = ( block_list[-1 - test_constants["DIFFICULTY_EPOCH"]].weight - block_list[-2 - test_constants["DIFFICULTY_EPOCH"]].weight) assert block_list[-1].header_block.proof_of_time curr_ips = calculate_ips_from_iterations( block_list[-1].header_block.proof_of_space, curr_difficulty, block_list[-1].header_block.proof_of_time.number_of_iterations, test_constants["MIN_BLOCK_TIME"], ) starting_height = block_list[-1].height + 1 timestamp = block_list[-1].header_block.header.data.timestamp for next_height in range(starting_height, starting_height + num_blocks): if (next_height > test_constants["DIFFICULTY_EPOCH"] and next_height % test_constants["DIFFICULTY_EPOCH"] == test_constants["DIFFICULTY_DELAY"]): # Calculates new difficulty height1 = uint64(next_height - (test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"]) - 1) height2 = uint64(next_height - (test_constants["DIFFICULTY_EPOCH"]) - 1) height3 = uint64(next_height - (test_constants["DIFFICULTY_DELAY"]) - 1) if height1 >= 0: block1 = block_list[height1] assert block1.header_block.challenge iters1 = block1.header_block.challenge.total_iters timestamp1 = block1.header_block.header.data.timestamp else: block1 = block_list[0] assert block1.header_block.challenge timestamp1 = (block1.header_block.header.data.timestamp - test_constants["BLOCK_TIME_TARGET"]) iters1 = block1.header_block.challenge.total_iters timestamp2 = block_list[ height2].header_block.header.data.timestamp timestamp3 = block_list[ height3].header_block.header.data.timestamp block3 = block_list[height3] assert block3.header_block.challenge iters3 = block3.header_block.challenge.total_iters term1 = (test_constants["DIFFICULTY_DELAY"] * prev_difficulty * (timestamp3 - timestamp2) * test_constants["BLOCK_TIME_TARGET"]) term2 = ((test_constants["DIFFICULTY_WARP_FACTOR"] - 1) * (test_constants["DIFFICULTY_EPOCH"] - test_constants["DIFFICULTY_DELAY"]) * curr_difficulty * (timestamp2 - timestamp1) * test_constants["BLOCK_TIME_TARGET"]) # Round down after the division new_difficulty: uint64 = uint64( (term1 + term2) // (test_constants["DIFFICULTY_WARP_FACTOR"] * (timestamp3 - timestamp2) * (timestamp2 - timestamp1))) if new_difficulty >= curr_difficulty: new_difficulty = min( new_difficulty, uint64(test_constants["DIFFICULTY_FACTOR"] * curr_difficulty), ) else: new_difficulty = max([ uint64(1), new_difficulty, uint64(curr_difficulty // test_constants["DIFFICULTY_FACTOR"]), ]) new_ips = uint64( (iters3 - iters1) // (timestamp3 - timestamp1)) if new_ips >= curr_ips: curr_ips = min( new_ips, uint64(test_constants["IPS_FACTOR"] * new_ips)) else: curr_ips = max([ uint64(1), new_ips, uint64(curr_ips // test_constants["IPS_FACTOR"]), ]) prev_difficulty = curr_difficulty curr_difficulty = new_difficulty time_taken = seconds_per_block timestamp += time_taken block_list.append( self.create_next_block( test_constants, block_list[-1], timestamp, curr_difficulty, curr_ips, seed, )) return block_list