Switch to signage point based iters
This commit is contained in:
parent
06109829a8
commit
a55649847f
|
@ -9,7 +9,7 @@ class ConsensusConstants:
|
|||
SLOT_SUB_BLOCKS_TARGET: uint32
|
||||
MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK: uint32
|
||||
MAX_SLOT_SUB_BLOCKS: uint32
|
||||
NUM_CHECKPOINTS_PER_SLOT: uint32
|
||||
NUM_SPS_SUB_SLOT: uint32
|
||||
IPS_STARTING: uint64
|
||||
|
||||
DIFFICULTY_STARTING: uint64
|
||||
|
@ -22,7 +22,7 @@ class ConsensusConstants:
|
|||
NUMBER_ZERO_BITS_PLOT_FILTER: int # H(plot signature of the challenge) must start with these many zeroes
|
||||
NUMBER_ZERO_BITS_SP_FILTER: int # H(plot signature of the sp) must start with these many zeroes
|
||||
SLOT_TIME_TARGET: int # The target number of seconds per block
|
||||
EXTRA_ITERS_TIME_TARGET: float
|
||||
NUM_SP_INTERVALS_EXTRA: int
|
||||
MAX_FUTURE_TIME: int # The next block can have a timestamp of at most these many seconds more
|
||||
NUMBER_OF_TIMESTAMPS: int # Than the average of the last NUMBER_OF_TIMESTAMPS blocks
|
||||
FIRST_CC_CHALLENGE: bytes
|
||||
|
|
|
@ -2,7 +2,12 @@ from .constants import ConsensusConstants
|
|||
|
||||
|
||||
testnet_kwargs = {
|
||||
"NUMBER_OF_HEADS": 3, # The number of tips each full node keeps track of and propagates
|
||||
# TODO(mariano): write comments here
|
||||
"SLOT_SUB_BLOCKS_TARGET": 16,
|
||||
"MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK": 5,
|
||||
"MAX_SLOT_SUB_BLOCKS": 64,
|
||||
"NUM_SPS_SUB_SLOT": 32,
|
||||
"IPS_STARTING": 2 ** 18,
|
||||
# DIFFICULTY_STARTING is the starting difficulty for the first epoch, which is then further
|
||||
# multiplied by another factor of 2^32, to be used in the VDF iter calculation formula.
|
||||
"DIFFICULTY_STARTING": 2 ** 20,
|
||||
|
@ -16,7 +21,7 @@ testnet_kwargs = {
|
|||
"NUMBER_ZERO_BITS_PLOT_FILTER": 3, # H(plot signature of the challenge) must start with these many zeroes
|
||||
"NUMBER_ZERO_BITS_SP_FILTER": 4, # H(plot signature of the challenge) must start with these many zeroes
|
||||
"SLOT_TIME_TARGET": 300, # The target number of seconds per slot
|
||||
"EXTRA_ITERS_TIME_TARGET": 37.5,
|
||||
"NUM_SP_INTERVALS_EXTRA": 3, # The number of sp intervals to add to the signage point
|
||||
"MAX_FUTURE_TIME": 7200, # The next block can have a timestamp of at most these many seconds more
|
||||
"NUMBER_OF_TIMESTAMPS": 11, # Than the average of the last NUMBER_OF_TIMESTAMPS blocks
|
||||
"FIRST_CC_CHALLENGE": bytes([0x00] * 32),
|
||||
|
|
|
@ -1,77 +1,60 @@
|
|||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.hash import std_hash
|
||||
from src.util.ints import uint64, uint128, uint8
|
||||
from src.consensus.pos_quality import quality_str_to_quality
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
|
||||
|
||||
def is_overflow_sub_block(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> bool:
|
||||
def is_overflow_sub_block(constants: ConsensusConstants, signage_point_index: uint8) -> bool:
|
||||
if signage_point_index >= constants.NUM_SPS_SUB_SLOT:
|
||||
raise ValueError("SP index too high")
|
||||
return signage_point_index >= constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA
|
||||
|
||||
|
||||
def calculate_sp_interval_iters(constants: ConsensusConstants, ips: uint64) -> uint64:
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips)
|
||||
if required_iters >= sub_slot_iters:
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
extra_iters: uint64 = uint64(int(float(ips) * constants.EXTRA_ITERS_TIME_TARGET))
|
||||
return required_iters + extra_iters >= sub_slot_iters
|
||||
return uint64(sub_slot_iters // constants.NUM_SPS_SUB_SLOT)
|
||||
|
||||
|
||||
def calculate_sub_slot_iters(constants: ConsensusConstants, ips: uint64) -> uint64:
|
||||
return ips * constants.SLOT_TIME_TARGET
|
||||
|
||||
|
||||
def calculate_sp_iters(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint64:
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips)
|
||||
if required_iters >= sub_slot_iters:
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
checkpoint_size: uint64 = uint64(sub_slot_iters // constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
checkpoint_index: int = required_iters // checkpoint_size
|
||||
|
||||
if checkpoint_index >= constants.NUM_CHECKPOINTS_PER_SLOT:
|
||||
# Checkpoints don't divide sub_slot_iters cleanly, so we return the last checkpoint
|
||||
return required_iters - required_iters % checkpoint_size - checkpoint_size
|
||||
else:
|
||||
return required_iters - required_iters % checkpoint_size
|
||||
def calculate_sp_iters(constants: ConsensusConstants, ips: uint64, signage_point_index: uint8) -> uint64:
|
||||
if signage_point_index >= constants.NUM_SPS_SUB_SLOT:
|
||||
raise ValueError("SP index too high")
|
||||
return uint64(calculate_sp_interval_iters(constants, ips) * signage_point_index)
|
||||
|
||||
|
||||
def calculate_sp_index(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint8:
|
||||
sp_iters = calculate_sp_iters(constants, ips, required_iters)
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips)
|
||||
checkpoint_size: uint64 = uint64(sub_slot_iters // constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
assert sp_iters % checkpoint_size == 0
|
||||
target_index = sp_iters // checkpoint_size
|
||||
return uint8(target_index)
|
||||
|
||||
|
||||
def calculate_ip_iters(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint64:
|
||||
def calculate_ip_iters(
|
||||
constants: ConsensusConstants, ips: uint64, signage_point_index: uint8, required_iters: uint64
|
||||
) -> uint64:
|
||||
# Note that the IPS is for the block passed in, which might be in the previous epoch
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips)
|
||||
if required_iters >= sub_slot_iters:
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
extra_iters: uint64 = uint64(int(float(ips) * constants.EXTRA_ITERS_TIME_TARGET))
|
||||
return (required_iters + extra_iters) % sub_slot_iters
|
||||
sp_iters = calculate_sp_iters(constants, ips, signage_point_index)
|
||||
sp_interval_iters: uint64 = calculate_sp_interval_iters(constants, ips)
|
||||
if sp_iters % sp_interval_iters != 0 or sp_iters >= sub_slot_iters:
|
||||
raise ValueError(f"Invalid sp iters {sp_iters} for this ips {ips}")
|
||||
|
||||
if required_iters >= sp_interval_iters or required_iters == 0:
|
||||
raise ValueError(
|
||||
f"Required iters {required_iters} is not below the sp interval iters {sp_interval_iters} {ips} or not >0."
|
||||
)
|
||||
|
||||
return (sp_iters + constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters) % sub_slot_iters
|
||||
|
||||
|
||||
def calculate_iterations_quality(
|
||||
quality: bytes32,
|
||||
quality_string: bytes32,
|
||||
size: int,
|
||||
difficulty: int,
|
||||
cc_sp_output_hash: bytes32,
|
||||
) -> uint64:
|
||||
"""
|
||||
Calculates the number of iterations from the quality. The quality is converted to a number
|
||||
between 0 and 1, then divided by expected plot size, and finally multiplied by the
|
||||
difficulty.
|
||||
"""
|
||||
iters = uint64(uint128(int(difficulty) << 32) // quality_str_to_quality(quality, size))
|
||||
sp_quality_string: bytes32 = std_hash(quality_string + cc_sp_output_hash)
|
||||
iters = uint64(uint128(int(difficulty) << 32) // quality_str_to_quality(sp_quality_string, size))
|
||||
return max(iters, uint64(1))
|
||||
|
||||
|
||||
def calculate_iterations(
|
||||
constants: ConsensusConstants,
|
||||
proof_of_space: ProofOfSpace,
|
||||
difficulty: int,
|
||||
) -> uint64:
|
||||
"""
|
||||
Convenience function to calculate the number of iterations using the proof instead
|
||||
of the quality. The quality must be retrieved from the proof.
|
||||
"""
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality_string(constants, None, None)
|
||||
assert quality is not None
|
||||
return calculate_iterations_quality(quality, proof_of_space.size, difficulty)
|
||||
|
|
181
src/farmer.py
181
src/farmer.py
|
@ -8,7 +8,7 @@ from src.util.keychain import Keychain
|
|||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import (
|
||||
calculate_iterations_quality,
|
||||
calculate_sp_index,
|
||||
calculate_sp_interval_iters,
|
||||
)
|
||||
from src.protocols import farmer_protocol, harvester_protocol
|
||||
from src.server.connection import PeerConnections
|
||||
|
@ -17,7 +17,7 @@ from src.types.proof_of_space import ProofOfSpace
|
|||
from src.types.sized_bytes import bytes32
|
||||
from src.types.pool_target import PoolTarget
|
||||
from src.util.api_decorators import api_request
|
||||
from src.util.ints import uint32, uint64, uint8
|
||||
from src.util.ints import uint32, uint64
|
||||
from src.wallet.derive_keys import master_sk_to_farmer_sk, master_sk_to_pool_sk
|
||||
from src.util.chech32 import decode_puzzle_hash
|
||||
|
||||
|
@ -38,14 +38,11 @@ class Farmer:
|
|||
consensus_constants: ConsensusConstants,
|
||||
):
|
||||
self.config = farmer_config
|
||||
# To send to harvester on connect
|
||||
self.latest_challenge: bytes32 = None
|
||||
|
||||
# Keep track of all sps for each challenge
|
||||
self.sps: Dict[bytes32, List[farmer_protocol.NewSignagePoint]] = {}
|
||||
|
||||
# Keep track of harvester plot identifier (str), target sp index, and PoSpace for each challenge
|
||||
self.proofs_of_space: Dict[bytes32, List[Tuple[str, uint8, ProofOfSpace]]] = {}
|
||||
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]] = {}
|
||||
|
@ -94,17 +91,8 @@ class Farmer:
|
|||
msg = harvester_protocol.HarvesterHandshake(
|
||||
self._get_public_keys(),
|
||||
self.pool_public_keys,
|
||||
self.config["pool_share_threshold"],
|
||||
)
|
||||
yield OutboundMessage(NodeType.HARVESTER, Message("harvester_handshake", msg), Delivery.RESPOND)
|
||||
if self.latest_challenge is not None and len(self.sps[self.latest_challenge]) > 0:
|
||||
sp = self.sps[self.latest_challenge][0]
|
||||
message = harvester_protocol.NewChallenge(sp.challenge_hash, sp.difficulty, sp.slot_iterations)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("new_challenge", message),
|
||||
Delivery.BROADCAST,
|
||||
)
|
||||
|
||||
def _set_global_connections(self, global_connections: PeerConnections):
|
||||
self.global_connections: PeerConnections = global_connections
|
||||
|
@ -129,93 +117,81 @@ class Farmer:
|
|||
return [master_sk_to_farmer_sk(sk) for sk, _ in all_sks] + [master_sk_to_pool_sk(sk) for sk, _ in all_sks]
|
||||
|
||||
@api_request
|
||||
async def challenge_response(self, challenge_response: harvester_protocol.ChallengeResponse):
|
||||
async def new_proof_of_space(self, new_proof_of_space: harvester_protocol.NewProofOfSpace):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
if challenge_response.proof.challenge_hash not in self.number_of_responses:
|
||||
self.number_of_responses[challenge_response.proof.challenge_hash] = 0
|
||||
if new_proof_of_space.proof.challenge_hash not in self.number_of_responses:
|
||||
self.number_of_responses[new_proof_of_space.proof.challenge_hash] = 0
|
||||
|
||||
if self.number_of_responses[challenge_response.proof.challenge_hash] >= 32:
|
||||
if self.number_of_responses[new_proof_of_space.proof.challenge_hash] >= 32:
|
||||
log.warning(
|
||||
f"Surpassed 32 PoSpace for one challenge, no longer submitting PoSpace for challenge "
|
||||
f"{challenge_response.proof.challenge_hash}"
|
||||
f"{new_proof_of_space.proof.challenge_hash}"
|
||||
)
|
||||
return
|
||||
|
||||
difficulty: uint64 = uint64(0)
|
||||
for sp in self.sps[challenge_response.proof.challenge_hash]:
|
||||
if sp.challenge_hash == challenge_response.proof.challenge_hash:
|
||||
for sp in self.sps[new_proof_of_space.proof.challenge_hash]:
|
||||
if sp.challenge_hash == new_proof_of_space.proof.challenge_hash:
|
||||
difficulty = sp.difficulty
|
||||
if difficulty == 0:
|
||||
log.error(f"Did not find challenge {challenge_response.proof.challenge_hash}")
|
||||
log.error(f"Did not find challenge {new_proof_of_space.proof.challenge_hash}")
|
||||
return
|
||||
|
||||
computed_quality_string = challenge_response.proof.verify_and_get_quality_string(self.constants, None, None)
|
||||
computed_quality_string = new_proof_of_space.proof.verify_and_get_quality_string(self.constants)
|
||||
if computed_quality_string is None:
|
||||
log.error(f"Invalid proof of space {challenge_response.proof}")
|
||||
log.error(f"Invalid proof of space {new_proof_of_space.proof}")
|
||||
return
|
||||
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
computed_quality_string,
|
||||
challenge_response.proof.size,
|
||||
difficulty,
|
||||
)
|
||||
|
||||
if challenge_response.proof.challenge_hash not in self.sps:
|
||||
log.warning(f"Received response for challenge that we do not have {challenge_response.challenge_hash}")
|
||||
if new_proof_of_space.proof.challenge_hash not in self.sps:
|
||||
log.warning(f"Received response for challenge that we do not have {new_proof_of_space.challenge_hash}")
|
||||
return
|
||||
elif len(self.sps[challenge_response.proof.challenge_hash]) == 0:
|
||||
log.warning(f"Received response for challenge {challenge_response.challenge_hash} with no sp data")
|
||||
elif len(self.sps[new_proof_of_space.proof.challenge_hash]) == 0:
|
||||
log.warning(f"Received response for challenge {new_proof_of_space.challenge_hash} with no sp data")
|
||||
return
|
||||
|
||||
sub_slot_iters = self.sps[challenge_response.proof.challenge_hash][0].slot_iterations
|
||||
|
||||
# Double check that the iters are good
|
||||
if required_iters < sub_slot_iters or required_iters < self.config["pool_share_threshold"]:
|
||||
self.number_of_responses[challenge_response.proof.challenge_hash] += 1
|
||||
self._state_changed("challenge")
|
||||
ips = sub_slot_iters // self.constants.SLOT_TIME_TARGET
|
||||
# This is the sp which this proof of space is assigned to
|
||||
target_sp_index: uint8 = calculate_sp_index(self.constants, ips, required_iters)
|
||||
self.number_of_responses[new_proof_of_space.proof.challenge_hash] += 1
|
||||
self._state_changed("challenge")
|
||||
# This is the sp which this proof of space is assigned to
|
||||
|
||||
# Requests signatures for the first sp (maybe the second if we were really slow at getting proofs)
|
||||
for sp in self.sps[challenge_response.proof.challenge_hash]:
|
||||
# If we already have the target sp, proceed at getting the signatures for this PoSpace
|
||||
if sp.signage_point_index == target_sp_index:
|
||||
request = harvester_protocol.RequestSignatures(
|
||||
challenge_response.plot_identifier,
|
||||
challenge_response.proof.challenge_hash,
|
||||
[sp.challenge_chain_sp, sp.reward_chain_sp],
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("request_signatures", request),
|
||||
Delivery.RESPOND,
|
||||
)
|
||||
if challenge_response.proof.challenge_hash not in self.proofs_of_space:
|
||||
self.proofs_of_space[challenge_response.proof.challenge_hash] = [
|
||||
(
|
||||
challenge_response.plot_identifier,
|
||||
target_sp_index,
|
||||
challenge_response.proof,
|
||||
)
|
||||
]
|
||||
else:
|
||||
self.proofs_of_space[challenge_response.proof.challenge_hash].append(
|
||||
(
|
||||
challenge_response.plot_identifier,
|
||||
target_sp_index,
|
||||
challenge_response.proof,
|
||||
)
|
||||
)
|
||||
self.quality_str_to_identifiers[computed_quality_string] = (
|
||||
challenge_response.plot_identifier,
|
||||
challenge_response.proof.challenge_hash,
|
||||
# Requests signatures for the first sp (maybe the second if we were really slow at getting proofs)
|
||||
for sp in self.sps[new_proof_of_space.proof.challenge_hash]:
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
computed_quality_string, new_proof_of_space.proof.size, difficulty, sp.challenge_hash
|
||||
)
|
||||
assert required_iters < calculate_sp_interval_iters(sp.slot_iterations, sp.ips)
|
||||
# If we already have the target sp, proceed at getting the signatures for this PoSpace
|
||||
request = harvester_protocol.RequestSignatures(
|
||||
new_proof_of_space.plot_identifier,
|
||||
new_proof_of_space.proof.challenge_hash,
|
||||
[sp.challenge_chain_sp, sp.reward_chain_sp],
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("request_signatures", request),
|
||||
Delivery.RESPOND,
|
||||
)
|
||||
if new_proof_of_space.proof.challenge_hash not in self.proofs_of_space:
|
||||
self.proofs_of_space[new_proof_of_space.proof.challenge_hash] = [
|
||||
(
|
||||
new_proof_of_space.plot_identifier,
|
||||
new_proof_of_space.proof,
|
||||
)
|
||||
]
|
||||
else:
|
||||
log.warning(f"Required_iters: {required_iters}, too high. Must be < sub_slot_iters={sub_slot_iters}")
|
||||
self.proofs_of_space[new_proof_of_space.proof.challenge_hash].append(
|
||||
(
|
||||
new_proof_of_space.plot_identifier,
|
||||
new_proof_of_space.proof,
|
||||
)
|
||||
)
|
||||
self.quality_str_to_identifiers[computed_quality_string] = (
|
||||
new_proof_of_space.plot_identifier,
|
||||
new_proof_of_space.proof.challenge_hash,
|
||||
)
|
||||
|
||||
@api_request
|
||||
async def respond_signatures(self, response: harvester_protocol.RespondSignatures):
|
||||
|
@ -335,17 +311,19 @@ class Farmer:
|
|||
|
||||
@api_request
|
||||
async def signage_point(self, signage_point: farmer_protocol.NewSignagePoint):
|
||||
message = harvester_protocol.NewSignagePoint(
|
||||
signage_point.challenge_hash,
|
||||
signage_point.difficulty,
|
||||
signage_point.ips,
|
||||
signage_point.signage_point_index,
|
||||
signage_point.challenge_chain_sp,
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("new_challenge", message),
|
||||
Delivery.BROADCAST,
|
||||
)
|
||||
if signage_point.challenge_hash not in self.seen_challenges:
|
||||
message = harvester_protocol.NewChallenge(
|
||||
signage_point.challenge_hash,
|
||||
signage_point.difficulty,
|
||||
signage_point.slot_iterations,
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("new_challenge", message),
|
||||
Delivery.BROADCAST,
|
||||
)
|
||||
self.seen_challenges.add(signage_point.challenge_hash)
|
||||
# This allows time for the collection of proofs from the harvester
|
||||
self._state_changed("challenge")
|
||||
|
@ -355,33 +333,10 @@ class Farmer:
|
|||
await asyncio.sleep(1)
|
||||
self._state_changed("challenge")
|
||||
|
||||
if self.latest_challenge != signage_point.challenge_hash:
|
||||
self.latest_challenge = signage_point.challenge_hash
|
||||
|
||||
if self.latest_challenge not in self.sps:
|
||||
self.sps[self.latest_challenge] = [signage_point]
|
||||
if signage_point.challenge_hash not in self.sps:
|
||||
self.sps[signage_point.challenge_hash] = [signage_point]
|
||||
else:
|
||||
self.sps[self.latest_challenge].append(signage_point)
|
||||
|
||||
# We already have fetched proofs for this challenge
|
||||
if signage_point.challenge_hash in self.proofs_of_space:
|
||||
for plot_identifier, target_sp_index, pospace in self.proofs_of_space[signage_point.challenge_hash]:
|
||||
if target_sp_index == signage_point.signage_point_index:
|
||||
# Only proceeds with proofs of space that can be infused at this infusion point
|
||||
request = harvester_protocol.RequestSignatures(
|
||||
plot_identifier,
|
||||
signage_point.challenge_hash,
|
||||
[
|
||||
signage_point.challenge_chain_sp,
|
||||
signage_point.reward_chain_sp,
|
||||
],
|
||||
)
|
||||
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
Message("request_signatures", request),
|
||||
Delivery.BROADCAST,
|
||||
)
|
||||
self.sps[signage_point.challenge_hash].append(signage_point)
|
||||
|
||||
@api_request
|
||||
async def request_signed_values(self, full_node_request: farmer_protocol.RequestSignedValues):
|
||||
|
|
|
@ -15,7 +15,6 @@ from src.full_node.cost_calculator import calculate_cost_of_program
|
|||
from src.full_node.mempool_check_conditions import get_name_puzzle_conditions
|
||||
from src.full_node.signage_point import SignagePoint
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
from src.types.coin import Coin, hash_coin_list
|
||||
from src.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from src.types.foliage import FoliageSubBlock, FoliageBlock, TransactionsInfo, FoliageSubBlockData
|
||||
|
@ -29,10 +28,9 @@ from src.types.spend_bundle import SpendBundle
|
|||
from src.types.unfinished_block import UnfinishedBlock
|
||||
from src.types.vdf import VDFInfo, VDFProof
|
||||
from src.util.hash import std_hash
|
||||
from src.util.ints import uint128, uint64, uint32
|
||||
from src.util.ints import uint128, uint64, uint32, uint8
|
||||
from src.util.merkle_set import MerkleSet
|
||||
from src.util.prev_block import get_prev_block
|
||||
from src.util.vdf_prover import get_vdf_info_and_proof
|
||||
from tests.recursive_replace import recursive_replace
|
||||
|
||||
|
||||
|
@ -200,6 +198,7 @@ def create_foliage(
|
|||
def create_unfinished_block(
|
||||
constants: ConsensusConstants,
|
||||
sub_slot_start_total_iters: uint128,
|
||||
signage_point_index: uint8,
|
||||
sp_iters: uint64,
|
||||
ip_iters: uint64,
|
||||
proof_of_space: ProofOfSpace,
|
||||
|
@ -208,14 +207,14 @@ def create_unfinished_block(
|
|||
pool_target: PoolTarget,
|
||||
get_plot_signature: Callable[[bytes32, G1Element], G2Element],
|
||||
get_pool_signature: Callable[[PoolTarget, G1Element], G2Element],
|
||||
signage_point: SignagePoint,
|
||||
timestamp: Optional[uint64] = None,
|
||||
seed: bytes32 = b"",
|
||||
spend_bundle: Optional[SpendBundle] = None,
|
||||
prev_sub_block: Optional[SubBlockRecord] = None,
|
||||
sub_blocks=None,
|
||||
finished_sub_slots=None,
|
||||
signage_point: Optional[SignagePoint] = None,
|
||||
) -> Optional[UnfinishedBlock]:
|
||||
) -> UnfinishedBlock:
|
||||
overflow = sp_iters > ip_iters
|
||||
total_iters_sp = sub_slot_start_total_iters + sp_iters
|
||||
prev_block: Optional[SubBlockRecord] = None # Set this if we are a block
|
||||
|
@ -235,83 +234,9 @@ def create_unfinished_block(
|
|||
cc_sp_hash: Optional[bytes32] = slot_cc_challenge
|
||||
|
||||
# Only enters this if statement if we are in testing mode (making VDF proofs here)
|
||||
if sp_iters != 0 and signage_point is None:
|
||||
if is_genesis:
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
if len(finished_sub_slots) == 0:
|
||||
cc_vdf_challenge = constants.FIRST_CC_CHALLENGE
|
||||
rc_vdf_challenge = constants.FIRST_RC_CHALLENGE
|
||||
else:
|
||||
cc_vdf_challenge = finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
rc_vdf_challenge = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
elif new_sub_slot and not overflow:
|
||||
# Start from start of this slot. Case of no overflow slots. Also includes genesis block after
|
||||
# empty slot (but not overflowing)
|
||||
rc_vdf_challenge: bytes32 = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
cc_vdf_challenge = finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
elif new_sub_slot and overflow and len(finished_sub_slots) > 1:
|
||||
# Start from start of prev slot. Rare case of empty prev slot.
|
||||
# Includes genesis block after 2 empty slots
|
||||
rc_vdf_challenge = finished_sub_slots[-2].reward_chain.get_hash()
|
||||
cc_vdf_challenge = finished_sub_slots[-2].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
else:
|
||||
if new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev will skip 1 sub-slot
|
||||
elif not new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 2 # Starting at prev does not skip any sub slots
|
||||
elif not new_sub_slot and not overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev does not skip any sub slots, but we should not go back
|
||||
else:
|
||||
assert False
|
||||
|
||||
next_sb: SubBlockRecord = prev_sub_block
|
||||
curr: SubBlockRecord = next_sb
|
||||
# Finds a sub-block which is BEFORE our signage point, otherwise goes back to the end of sub-slot
|
||||
# Note that for overflow sub-blocks, we are looking at the end of the previous sub-slot
|
||||
while num_sub_slots_to_look_for > 0:
|
||||
next_sb = curr
|
||||
if curr.first_in_sub_slot:
|
||||
num_sub_slots_to_look_for -= 1
|
||||
if curr.total_iters < total_iters_sp:
|
||||
break
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
if curr.total_iters < total_iters_sp:
|
||||
sp_vdf_iters = total_iters_sp - curr.total_iters
|
||||
cc_vdf_input = curr.challenge_vdf_output
|
||||
rc_vdf_challenge = curr.reward_infusion_new_challenge
|
||||
else:
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
rc_vdf_challenge = next_sb.finished_reward_slot_hashes[-1]
|
||||
|
||||
while not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
cc_vdf_challenge = curr.finished_challenge_slot_hashes[-1]
|
||||
|
||||
cc_sp_vdf, cc_sp_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
cc_vdf_input,
|
||||
cc_vdf_challenge,
|
||||
sp_vdf_iters,
|
||||
)
|
||||
rc_sp_vdf, rc_sp_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
rc_vdf_challenge,
|
||||
sp_vdf_iters,
|
||||
)
|
||||
|
||||
cc_sp_hash = cc_sp_vdf.output.get_hash()
|
||||
rc_sp_hash = rc_sp_vdf.output.get_hash()
|
||||
signage_point = SignagePoint(cc_sp_vdf, cc_sp_proof, rc_sp_vdf, rc_sp_proof)
|
||||
if signage_point.cc_vdf is not None:
|
||||
cc_sp_hash = signage_point.cc_vdf.output.get_hash()
|
||||
rc_sp_hash = signage_point.rc_vdf.output.get_hash()
|
||||
else:
|
||||
if new_sub_slot:
|
||||
rc_sp_hash = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
|
@ -331,16 +256,14 @@ def create_unfinished_block(
|
|||
assert rc_sp_signature is not None
|
||||
assert blspy.AugSchemeMPL.verify(proof_of_space.plot_public_key, cc_sp_hash, cc_sp_signature)
|
||||
|
||||
# Checks sp filter
|
||||
plot_id = proof_of_space.get_plot_id()
|
||||
if not ProofOfSpace.can_create_proof(
|
||||
constants, plot_id, proof_of_space.challenge_hash, cc_sp_hash, cc_sp_signature
|
||||
):
|
||||
return None
|
||||
# print(
|
||||
# f"Calculating iters.. sssti {sub_slot_start_total_iters} ip iters {ip_iters} overflow {overflow} {prev_sub_slot_iters}"
|
||||
# )
|
||||
total_iters = uint128(sub_slot_start_total_iters + ip_iters + (prev_sub_slot_iters if overflow else 0))
|
||||
|
||||
rc_sub_block = RewardChainSubBlockUnfinished(
|
||||
total_iters,
|
||||
signage_point_index,
|
||||
proof_of_space,
|
||||
signage_point.cc_vdf,
|
||||
cc_sp_signature,
|
||||
|
@ -405,6 +328,7 @@ def unfinished_block_to_full_block(
|
|||
new_weight,
|
||||
new_height,
|
||||
unfinished_block.reward_chain_sub_block.total_iters,
|
||||
unfinished_block.reward_chain_sub_block.signage_point_index,
|
||||
unfinished_block.reward_chain_sub_block.proof_of_space,
|
||||
unfinished_block.reward_chain_sub_block.challenge_chain_sp_vdf,
|
||||
unfinished_block.reward_chain_sub_block.challenge_chain_sp_signature,
|
||||
|
|
|
@ -78,8 +78,6 @@ async def validate_unfinished_header_block(
|
|||
|
||||
q_str: Optional[bytes32] = header_block.reward_chain_sub_block.proof_of_space.verify_and_get_quality_string(
|
||||
constants,
|
||||
cc_sp_hash,
|
||||
header_block.reward_chain_sub_block.challenge_chain_sp_signature,
|
||||
)
|
||||
if q_str is None:
|
||||
return None, ValidationError(Err.INVALID_POSPACE)
|
||||
|
@ -89,14 +87,23 @@ async def validate_unfinished_header_block(
|
|||
q_str,
|
||||
header_block.reward_chain_sub_block.proof_of_space.size,
|
||||
difficulty,
|
||||
cc_sp_hash,
|
||||
)
|
||||
# 3. check signage point index
|
||||
if (
|
||||
header_block.reward_chain_sub_block.signage_point_index < 0
|
||||
or header_block.reward_chain_sub_block.signage_point_index >= constants.NUM_SPS_SUB_SLOT
|
||||
):
|
||||
return None, ValidationError(Err.INVALID_SP_INDEX)
|
||||
|
||||
sp_iters: uint64 = calculate_sp_iters(constants, ips, required_iters)
|
||||
ip_iters: uint64 = calculate_ip_iters(constants, ips, required_iters)
|
||||
sp_iters: uint64 = calculate_sp_iters(constants, ips, header_block.reward_chain_sub_block.signage_point_index)
|
||||
ip_iters: uint64 = calculate_ip_iters(
|
||||
constants, ips, header_block.reward_chain_sub_block.signage_point_index, required_iters
|
||||
)
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips)
|
||||
overflow = is_overflow_sub_block(constants, ips, required_iters)
|
||||
overflow = is_overflow_sub_block(constants, header_block.reward_chain_sub_block.signage_point_index)
|
||||
|
||||
# 3. Check finished slots that have been crossed since prev_sb
|
||||
# 4. Check finished slots that have been crossed since prev_sb
|
||||
ses_hash: Optional[bytes32] = None
|
||||
if new_sub_slot:
|
||||
# Finished a slot(s) since previous block. The first sub-slot must have at least one sub-block, and all
|
||||
|
@ -107,7 +114,7 @@ async def validate_unfinished_header_block(
|
|||
|
||||
if finished_sub_slot_n == 0:
|
||||
if genesis_block:
|
||||
# 3a. check sub-slot challenge hash for genesis block
|
||||
# 4a. check sub-slot challenge hash for genesis block
|
||||
if challenge_hash != constants.FIRST_CC_CHALLENGE:
|
||||
return None, ValidationError(Err.INVALID_PREV_CHALLENGE_SLOT_HASH)
|
||||
else:
|
||||
|
@ -115,11 +122,11 @@ async def validate_unfinished_header_block(
|
|||
while not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
# 3b. check sub-slot challenge hash for non-genesis block
|
||||
# 4b. check sub-slot challenge hash for non-genesis block
|
||||
if not curr.finished_challenge_slot_hashes[-1] == challenge_hash:
|
||||
return None, ValidationError(Err.INVALID_PREV_CHALLENGE_SLOT_HASH)
|
||||
else:
|
||||
# 3c. check sub-slot challenge hash for empty slot
|
||||
# 4c. check sub-slot challenge hash for empty slot
|
||||
if (
|
||||
not header_block.finished_sub_slots[finished_sub_slot_n - 1].challenge_chain.get_hash()
|
||||
== challenge_hash
|
||||
|
@ -127,7 +134,7 @@ async def validate_unfinished_header_block(
|
|||
return None, ValidationError(Err.INVALID_PREV_CHALLENGE_SLOT_HASH)
|
||||
|
||||
if genesis_block:
|
||||
# 3d. Validate that genesis block has no ICC
|
||||
# 4d. Validate that genesis block has no ICC
|
||||
if sub_slot.infused_challenge_chain is not None:
|
||||
return None, ValidationError(Err.SHOULD_NOT_HAVE_ICC)
|
||||
else:
|
||||
|
@ -145,15 +152,15 @@ async def validate_unfinished_header_block(
|
|||
curr = sub_blocks[curr.prev_hash]
|
||||
if curr.is_challenge_sub_block(constants):
|
||||
icc_challenge_hash = curr.challenge_block_info_hash
|
||||
ip_iters_challenge_block = calculate_ip_iters(constants, curr.ips, curr.required_iters)
|
||||
icc_iters_committed: uint64 = (
|
||||
calculate_sub_slot_iters(constants, prev_sb.ips) - ip_iters_challenge_block
|
||||
)
|
||||
icc_iters_committed: uint64 = calculate_sub_slot_iters(
|
||||
constants, prev_sb.ips
|
||||
) - curr.ip_iters(constants)
|
||||
else:
|
||||
icc_challenge_hash = curr.finished_infused_challenge_slot_hashes[-1]
|
||||
icc_iters_committed = calculate_sub_slot_iters(constants, prev_sb.ips)
|
||||
ip_iters_prev = calculate_ip_iters(constants, prev_sb.ips, prev_sb.required_iters)
|
||||
icc_iters_proof: uint64 = calculate_sub_slot_iters(constants, prev_sb.ips) - ip_iters_prev
|
||||
icc_iters_proof: uint64 = calculate_sub_slot_iters(constants, prev_sb.ips) - prev_sb.ip_iters(
|
||||
constants
|
||||
)
|
||||
if prev_sb.is_challenge_sub_block(constants):
|
||||
icc_vdf_input = ClassgroupElement.get_default_element()
|
||||
else:
|
||||
|
@ -172,11 +179,11 @@ async def validate_unfinished_header_block(
|
|||
icc_iters_proof = icc_iters_committed
|
||||
icc_vdf_input = ClassgroupElement.get_default_element()
|
||||
|
||||
# 3e. Validate that there is not icc iff icc_challenge hash is None
|
||||
# 4e. Validate that there is not icc iff icc_challenge hash is None
|
||||
assert (sub_slot.infused_challenge_chain is None) == (icc_challenge_hash is None)
|
||||
if sub_slot.infused_challenge_chain is not None:
|
||||
assert icc_vdf_input is not None
|
||||
# 2c. Check infused challenge chain sub-slot VDF
|
||||
# 4f. Check infused challenge chain sub-slot VDF
|
||||
# Only validate from prev_sb to optimize
|
||||
target_vdf_info = VDFInfo(
|
||||
icc_challenge_hash,
|
||||
|
@ -195,7 +202,7 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_ICC_EOS_VDF)
|
||||
|
||||
# 2d. Check infused challenge sub-slot hash in challenge sub-slot
|
||||
# 4g. Check infused challenge sub-slot hash in challenge sub-slot
|
||||
if sub_slot.reward_chain.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
|
||||
if (
|
||||
sub_slot.infused_challenge_chain.get_hash()
|
||||
|
@ -206,7 +213,7 @@ async def validate_unfinished_header_block(
|
|||
if sub_slot.challenge_chain.infused_challenge_chain_sub_slot_hash is not None:
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_CC)
|
||||
|
||||
# 2e. Check infused challenge sub-slot hash in reward sub-slot
|
||||
# 4h. Check infused challenge sub-slot hash in reward sub-slot
|
||||
if (
|
||||
sub_slot.infused_challenge_chain.get_hash()
|
||||
!= sub_slot.reward_chain.infused_challenge_chain_sub_slot_hash
|
||||
|
@ -223,12 +230,12 @@ async def validate_unfinished_header_block(
|
|||
assert ses_hash is None # Only one of the slots can have it
|
||||
ses_hash = sub_slot.challenge_chain.subepoch_summary_hash
|
||||
|
||||
# 2f. check sub-epoch summary hash is None for empty slots
|
||||
# 4i. check sub-epoch summary hash is None for empty slots
|
||||
if finished_sub_slot_n != 0:
|
||||
if sub_slot.challenge_chain.subepoch_summary_hash is not None:
|
||||
return None, ValidationError(Err.INVALID_SUB_EPOCH_SUMMARY_HASH)
|
||||
|
||||
# 2g. Check new difficulty
|
||||
# 4j. Check new difficulty
|
||||
if finishes_epoch:
|
||||
if sub_slot.challenge_chain.new_ips != ips:
|
||||
return None, ValidationError(Err.INVALID_NEW_IPS)
|
||||
|
@ -240,14 +247,14 @@ async def validate_unfinished_header_block(
|
|||
if sub_slot.challenge_chain.new_difficulty is not None:
|
||||
return None, ValidationError(Err.INVALID_NEW_DIFFICULTY)
|
||||
|
||||
# 2h. Check challenge sub-slot hash in reward sub-slot
|
||||
# 4k. 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"
|
||||
)
|
||||
|
||||
# 2i. Check challenge chain sub-slot VDF
|
||||
# 2j. Check end of reward slot VDF
|
||||
# 4l. Check challenge chain sub-slot VDF
|
||||
# 4m. Check end of reward slot VDF
|
||||
sub_slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
eos_vdf_iters: uint64 = sub_slot_iters
|
||||
cc_start_element: ClassgroupElement = ClassgroupElement.get_default_element()
|
||||
|
@ -268,7 +275,7 @@ async def validate_unfinished_header_block(
|
|||
# the same IPS as the previous block, since it's the same slot
|
||||
rc_eos_vdf_challenge: bytes32 = prev_sb.reward_infusion_new_challenge
|
||||
sub_slot_iters = calculate_sub_slot_iters(constants, prev_sb.ips)
|
||||
eos_vdf_iters = sub_slot_iters - calculate_ip_iters(constants, prev_sb.ips, prev_sb.required_iters)
|
||||
eos_vdf_iters = sub_slot_iters - prev_sb.ip_iters(constants)
|
||||
cc_start_element: ClassgroupElement = prev_sb.challenge_vdf_output
|
||||
else:
|
||||
# At least one empty slot, so use previous slot hash. IPS might change because it's a new slot
|
||||
|
@ -304,7 +311,7 @@ async def validate_unfinished_header_block(
|
|||
if not sub_slot.proofs.challenge_chain_slot_proof.is_valid(constants, partial_cc_vdf_info, None):
|
||||
return None, ValidationError(Err.INVALID_CC_EOS_VDF)
|
||||
|
||||
# 2k. Check deficit (5 deficit edge case for genesis block)
|
||||
# 4n. Check deficit (5 deficit edge case for genesis block)
|
||||
if genesis_block:
|
||||
if sub_slot.reward_chain.deficit != constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
|
||||
return None, ValidationError(
|
||||
|
@ -326,25 +333,25 @@ async def validate_unfinished_header_block(
|
|||
if sub_slot.reward_chain.deficit != prev_sb.deficit:
|
||||
return None, ValidationError(Err.INVALID_DEFICIT, "deficit is wrong at slot end")
|
||||
|
||||
# 3. Check sub-epoch summary
|
||||
# 5. Check sub-epoch summary
|
||||
# Note that the subepoch summary is the summary of the previous subepoch (not the one that just finished)
|
||||
if ses_hash is not None:
|
||||
# 3a. Check that genesis block does not have sub-epoch summary
|
||||
# 5a. Check that genesis block does not have sub-epoch summary
|
||||
if genesis_block:
|
||||
return None, ValidationError(Err.INVALID_SUB_EPOCH_SUMMARY, "genesis with sub-epoch-summary hash")
|
||||
|
||||
# 3b. Check that we finished a slot and we finished a sub-epoch
|
||||
# 5b. Check that we finished a slot and we finished a sub-epoch
|
||||
if not new_sub_slot or not finishes_se:
|
||||
return None, ValidationError(
|
||||
Err.INVALID_SUB_EPOCH_SUMMARY, f"new sub-slot: {new_sub_slot} finishes sub-epoch {finishes_se}"
|
||||
)
|
||||
|
||||
# 3c. Check the actual sub-epoch is correct
|
||||
# 5c. Check the actual sub-epoch is correct
|
||||
expected_sub_epoch_summary = make_sub_epoch_summary(
|
||||
constants,
|
||||
sub_blocks,
|
||||
uint32(prev_sb.height + 1),
|
||||
prev_sb,
|
||||
sub_blocks[prev_sb.prev_hash],
|
||||
difficulty if finishes_epoch else None,
|
||||
ips if finishes_epoch else None,
|
||||
)
|
||||
|
@ -354,7 +361,7 @@ async def validate_unfinished_header_block(
|
|||
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
|
||||
# 5d. Check that we don't have to include a sub-epoch summary
|
||||
if finishes_sub_epoch(constants, prev_sb.height, prev_sb.deficit, False, sub_blocks, prev_sb.prev_hash):
|
||||
return None, ValidationError(
|
||||
Err.INVALID_SUB_EPOCH_SUMMARY, "block finishes sub-epoch but ses-hash is None"
|
||||
|
@ -364,7 +371,7 @@ async def validate_unfinished_header_block(
|
|||
# Blocks with very low required iters are not overflow blocks
|
||||
assert not overflow
|
||||
|
||||
# 5. Check no overflows in the first sub-slot of a new epoch (although they are OK in the second sub-slot)
|
||||
# 6. Check no overflows in the first sub-slot of a new epoch (although they are OK in the second sub-slot)
|
||||
if overflow and finishes_epoch and len(header_block.finished_sub_slots) < 2:
|
||||
return None, ValidationError(Err.NO_OVERFLOWS_IN_FIRST_SUB_SLOT_NEW_EPOCH)
|
||||
|
||||
|
@ -399,58 +406,62 @@ async def validate_unfinished_header_block(
|
|||
challenge = reversed_challenge_hashes[challenges_to_look_for - 1]
|
||||
assert challenge is not None
|
||||
|
||||
# 6. Check challenge in proof of space is valid
|
||||
# 7. Check challenge in proof of space is valid
|
||||
if challenge != header_block.reward_chain_sub_block.proof_of_space.challenge_hash:
|
||||
return None, ValidationError(Err.INVALID_POSPACE_CHALLENGE)
|
||||
|
||||
# 7. Check total iters
|
||||
# 8. Check total iters
|
||||
if genesis_block:
|
||||
total_iters: uint128 = uint128(
|
||||
constants.IPS_STARTING * constants.SLOT_TIME_TARGET * len(header_block.finished_sub_slots)
|
||||
)
|
||||
else:
|
||||
prev_sb_iters = calculate_ip_iters(constants, prev_sb.ips, prev_sb.required_iters)
|
||||
if new_sub_slot:
|
||||
total_iters: uint128 = prev_sb.total_iters
|
||||
prev_sb_slot_iters = calculate_sub_slot_iters(constants, prev_sb.ips)
|
||||
# Add the rest of the slot of prev_sb
|
||||
total_iters += prev_sb_slot_iters - prev_sb_iters
|
||||
total_iters += prev_sb_slot_iters - prev_sb.ip_iters(constants)
|
||||
# Add other empty slots
|
||||
total_iters += sub_slot_iters * (len(header_block.finished_sub_slots) - 1)
|
||||
else:
|
||||
# Slot iters is guaranteed to be the same for header_block and prev_sb
|
||||
# This takes the beginning of the slot, and adds ip_iters
|
||||
total_iters = uint128(prev_sb.total_iters - prev_sb_iters)
|
||||
total_iters = uint128(prev_sb.total_iters - prev_sb.ip_iters(constants))
|
||||
total_iters += ip_iters
|
||||
if total_iters != header_block.reward_chain_sub_block.total_iters:
|
||||
return None, ValidationError(Err.INVALID_TOTAL_ITERS)
|
||||
|
||||
if new_sub_slot and not overflow:
|
||||
# Start from start of this slot. Case of no overflow slots. Also includes genesis block after empty slot(s),
|
||||
# but not overflowing
|
||||
# Case 1: start from start of this slot. Case of no overflow slots. Also includes genesis block after empty
|
||||
# slot(s), but not overflowing
|
||||
rc_vdf_challenge: bytes32 = header_block.finished_sub_slots[-1].reward_chain.get_hash()
|
||||
cc_vdf_challenge = header_block.finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
elif new_sub_slot and overflow and len(header_block.finished_sub_slots) > 1:
|
||||
# Start from start of prev slot. Rare case of empty prev slot. Includes genesis block after 2 empty slots
|
||||
# Case 2: start from start of prev slot. This is a rare case of empty prev slot. Includes genesis block after
|
||||
# 2 empty slots
|
||||
rc_vdf_challenge = header_block.finished_sub_slots[-2].reward_chain.get_hash()
|
||||
cc_vdf_challenge = header_block.finished_sub_slots[-2].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
elif genesis_block:
|
||||
# Genesis block case, first challenge
|
||||
# Case 3: Genesis block case, first challenge
|
||||
rc_vdf_challenge = constants.FIRST_RC_CHALLENGE
|
||||
cc_vdf_challenge = constants.FIRST_CC_CHALLENGE
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
else:
|
||||
if new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev will skip 1 sub-slot
|
||||
# Case 4: Starting at prev will put us in the previous, sub-slot, since case 2 handled more empty slots
|
||||
num_sub_slots_to_look_for = 1
|
||||
elif not new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 2 # Starting at prev does not skip any sub slots
|
||||
# Case 5: prev is in the same sub slot and also overflow. Starting at prev does not skip any sub slots
|
||||
num_sub_slots_to_look_for = 2
|
||||
elif not new_sub_slot and not overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev does not skip any sub slots, but we should not go back
|
||||
# Case 6: prev is in the same sub slot. Starting at prev does not skip any sub slots. We do not need
|
||||
# to go back another sub slot, because it's not overflow, so the VDF to signage point is this sub-slot.
|
||||
num_sub_slots_to_look_for = 1
|
||||
else:
|
||||
assert False
|
||||
sp_total_iters = total_iters - ip_iters + sp_iters
|
||||
|
@ -463,6 +474,8 @@ async def validate_unfinished_header_block(
|
|||
while num_sub_slots_to_look_for > 0:
|
||||
if curr.first_in_sub_slot:
|
||||
num_sub_slots_to_look_for -= 1
|
||||
if num_sub_slots_to_look_for == 0:
|
||||
break
|
||||
if curr.total_iters < sp_total_iters:
|
||||
break
|
||||
if curr.height == 0:
|
||||
|
@ -482,7 +495,7 @@ async def validate_unfinished_header_block(
|
|||
curr = sub_blocks[curr.prev_hash]
|
||||
cc_vdf_challenge = curr.finished_challenge_slot_hashes[-1]
|
||||
|
||||
# 8. Check reward chain sp proof
|
||||
# 9. Check reward chain sp proof
|
||||
if sp_iters != 0:
|
||||
target_vdf_info = VDFInfo(
|
||||
rc_vdf_challenge,
|
||||
|
@ -515,7 +528,7 @@ async def validate_unfinished_header_block(
|
|||
curr = sub_blocks[curr.prev_hash]
|
||||
rc_sp_hash = curr.finished_reward_slot_hashes[-1]
|
||||
|
||||
# 9. Check reward chain sp signature
|
||||
# 10. Check reward chain sp signature
|
||||
if not AugSchemeMPL.verify(
|
||||
header_block.reward_chain_sub_block.proof_of_space.plot_public_key,
|
||||
rc_sp_hash,
|
||||
|
@ -524,7 +537,7 @@ async def validate_unfinished_header_block(
|
|||
log.error("block %s failed validation, rc sp sig validation %s, ", header_block.header_hash)
|
||||
return None, ValidationError(Err.INVALID_RC_SIGNATURE)
|
||||
|
||||
# 10. Check cc sp
|
||||
# 11. Check cc sp
|
||||
if sp_iters != 0:
|
||||
target_vdf_info = VDFInfo(
|
||||
cc_vdf_challenge,
|
||||
|
@ -546,7 +559,7 @@ async def validate_unfinished_header_block(
|
|||
log.error("block %s failed validation, overflow should not include cc vdf, ", header_block.header_hash)
|
||||
return None, ValidationError(Err.INVALID_CC_SP_VDF)
|
||||
|
||||
# 11. Check cc sp sig
|
||||
# 12. Check cc sp sig
|
||||
if not AugSchemeMPL.verify(
|
||||
header_block.reward_chain_sub_block.proof_of_space.plot_public_key,
|
||||
cc_sp_hash,
|
||||
|
@ -554,7 +567,7 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_CC_SIGNATURE, "invalid cc sp sig")
|
||||
|
||||
# 12. Check is_block
|
||||
# 13. Check is_block
|
||||
if genesis_block:
|
||||
if header_block.foliage_sub_block.foliage_block_hash is None:
|
||||
return None, ValidationError(Err.INVALID_IS_BLOCK, "invalid genesis")
|
||||
|
@ -572,7 +585,7 @@ async def validate_unfinished_header_block(
|
|||
if (our_sp_total_iters > curr.total_iters) != (header_block.foliage_sub_block.foliage_block_hash is not None):
|
||||
return None, ValidationError(Err.INVALID_IS_BLOCK)
|
||||
|
||||
# 13. Check foliage sub block signature by plot key
|
||||
# 14. Check foliage sub block signature by plot key
|
||||
if not AugSchemeMPL.verify(
|
||||
header_block.reward_chain_sub_block.proof_of_space.plot_public_key,
|
||||
header_block.foliage_sub_block.foliage_sub_block_data.get_hash(),
|
||||
|
@ -580,7 +593,7 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_PLOT_SIGNATURE)
|
||||
|
||||
# 14. Check foliage block signature by plot key
|
||||
# 15. Check foliage block signature by plot key
|
||||
if header_block.foliage_sub_block.foliage_block_hash is not None:
|
||||
if not AugSchemeMPL.verify(
|
||||
header_block.reward_chain_sub_block.proof_of_space.plot_public_key,
|
||||
|
@ -589,21 +602,21 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_PLOT_SIGNATURE)
|
||||
|
||||
# 15. Check unfinished reward chain sub block hash
|
||||
# 16. Check unfinished reward chain sub block hash
|
||||
if (
|
||||
header_block.reward_chain_sub_block.get_hash()
|
||||
!= header_block.foliage_sub_block.foliage_sub_block_data.unfinished_reward_block_hash
|
||||
):
|
||||
return None, ValidationError(Err.INVALID_URSB_HASH)
|
||||
|
||||
# 16. Check pool target max height
|
||||
# 17. Check pool target max height
|
||||
if (
|
||||
header_block.foliage_sub_block.foliage_sub_block_data.pool_target.max_height != 0
|
||||
and header_block.foliage_sub_block.foliage_sub_block_data.pool_target.max_height < height
|
||||
):
|
||||
return None, ValidationError(Err.OLD_POOL_TARGET)
|
||||
|
||||
# 17. Check pool target signature
|
||||
# 18. Check pool target signature
|
||||
if not AugSchemeMPL.verify(
|
||||
header_block.reward_chain_sub_block.proof_of_space.pool_public_key,
|
||||
bytes(header_block.foliage_sub_block.foliage_sub_block_data.pool_target),
|
||||
|
@ -611,8 +624,8 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_POOL_SIGNATURE)
|
||||
|
||||
# 18. Check extension data if applicable. None for mainnet.
|
||||
# 19. Check if foliage block is present
|
||||
# 19. Check extension data if applicable. None for mainnet.
|
||||
# 20. Check if foliage block is present
|
||||
if (header_block.foliage_sub_block.foliage_block_hash is not None) != (header_block.foliage_block is not None):
|
||||
return None, ValidationError(Err.INVALID_FOLIAGE_BLOCK_PRESENCE)
|
||||
|
||||
|
@ -620,11 +633,11 @@ async def validate_unfinished_header_block(
|
|||
return None, ValidationError(Err.INVALID_FOLIAGE_BLOCK_PRESENCE)
|
||||
|
||||
if header_block.foliage_block is not None:
|
||||
# 20. Check foliage block hash
|
||||
# 21. Check foliage block hash
|
||||
if header_block.foliage_block.get_hash() != header_block.foliage_sub_block.foliage_block_hash:
|
||||
return None, ValidationError(Err.INVALID_FOLIAGE_BLOCK_HASH)
|
||||
|
||||
# 21. Check prev block hash
|
||||
# 22. Check prev block hash
|
||||
if genesis_block:
|
||||
if header_block.foliage_block.prev_block_hash != bytes([0] * 32):
|
||||
return None, ValidationError(Err.INVALID_PREV_BLOCK_HASH)
|
||||
|
@ -635,12 +648,12 @@ async def validate_unfinished_header_block(
|
|||
if not header_block.foliage_block.prev_block_hash == curr_sb.header_hash:
|
||||
return None, ValidationError(Err.INVALID_PREV_BLOCK_HASH)
|
||||
|
||||
# 22. The filter hash in the Foliage Block must be the hash of the filter
|
||||
# 23. The filter hash in the Foliage Block must be the hash of the filter
|
||||
if check_filter:
|
||||
if header_block.foliage_block.filter_hash != std_hash(header_block.transactions_filter):
|
||||
return None, ValidationError(Err.INVALID_TRANSACTIONS_FILTER_HASH)
|
||||
|
||||
# 23. The timestamp in Foliage Block must comply with the timestamp rules
|
||||
# 24. The timestamp in Foliage Block must comply with the timestamp rules
|
||||
if prev_sb is not None:
|
||||
last_timestamps: List[uint64] = []
|
||||
curr_sb: SubBlockRecord = sub_blocks[header_block.foliage_block.prev_block_hash]
|
||||
|
@ -697,14 +710,16 @@ async def validate_finished_header_block(
|
|||
prev_sb: Optional[SubBlockRecord] = sub_blocks[header_block.prev_header_hash]
|
||||
new_sub_slot: bool = len(header_block.finished_sub_slots) > 0
|
||||
ips, difficulty = get_ips_and_difficulty(constants, unfinished_header_block, height_to_hash, prev_sb, sub_blocks)
|
||||
ip_iters: uint64 = calculate_ip_iters(constants, ips, required_iters)
|
||||
ip_iters: uint64 = calculate_ip_iters(
|
||||
constants, ips, header_block.reward_chain_sub_block.signage_point_index, required_iters
|
||||
)
|
||||
|
||||
if not genesis_block:
|
||||
# 24. Check sub-block height
|
||||
# 25. Check sub-block height
|
||||
if header_block.height != prev_sb.height + 1:
|
||||
return None, ValidationError(Err.INVALID_HEIGHT)
|
||||
|
||||
# 25. Check weight
|
||||
# 26. Check weight
|
||||
if header_block.weight != prev_sb.weight + difficulty:
|
||||
return None, ValidationError(Err.INVALID_WEIGHT)
|
||||
else:
|
||||
|
@ -734,7 +749,7 @@ async def validate_finished_header_block(
|
|||
ip_vdf_iters: uint64 = uint64(header_block.reward_chain_sub_block.total_iters - prev_sb.total_iters)
|
||||
cc_vdf_output = prev_sb.challenge_vdf_output
|
||||
|
||||
# 26. Check challenge chain infusion point VDF
|
||||
# 27. Check challenge chain infusion point VDF
|
||||
if new_sub_slot:
|
||||
cc_vdf_challenge = header_block.finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
else:
|
||||
|
@ -762,7 +777,7 @@ async def validate_finished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_CC_IP_VDF)
|
||||
|
||||
# 27. Check reward chain infusion point VDF
|
||||
# 28. Check reward chain infusion point VDF
|
||||
rc_target_vdf_info = VDFInfo(
|
||||
rc_vdf_challenge,
|
||||
ClassgroupElement.get_default_element(),
|
||||
|
@ -776,9 +791,9 @@ async def validate_finished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_RC_IP_VDF)
|
||||
|
||||
# 28. Check infused challenge chain infusion point VDF
|
||||
# 29. Check infused challenge chain infusion point VDF
|
||||
if not genesis_block:
|
||||
overflow = is_overflow_sub_block(constants, ips, required_iters)
|
||||
overflow = is_overflow_sub_block(constants, header_block.reward_chain_sub_block.signage_point_index)
|
||||
deficit = calculate_deficit(
|
||||
constants, header_block.height, prev_sb, overflow, len(header_block.finished_sub_slots) > 0
|
||||
)
|
||||
|
@ -835,11 +850,11 @@ async def validate_finished_header_block(
|
|||
if header_block.infused_challenge_chain_ip_proof is not None:
|
||||
return None, ValidationError(Err.INVALID_ICC_VDF)
|
||||
|
||||
# 29. Check reward block hash
|
||||
# 30. Check reward block hash
|
||||
if header_block.foliage_sub_block.reward_block_hash != header_block.reward_chain_sub_block.get_hash():
|
||||
return None, ValidationError(Err.INVALID_REWARD_BLOCK_HASH)
|
||||
|
||||
# 30. Check reward block is_block
|
||||
# 31. Check reward block is_block
|
||||
if (header_block.foliage_sub_block.foliage_block_hash is not None) != header_block.reward_chain_sub_block.is_block:
|
||||
return None, ValidationError(Err.INVALID_FOLIAGE_BLOCK_PRESENCE)
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import multiprocessing
|
|||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_ip_iters, calculate_sp_iters
|
||||
from src.full_node.block_body_validation import validate_block_body
|
||||
from src.full_node.block_store import BlockStore
|
||||
from src.full_node.coin_store import CoinStore
|
||||
|
@ -20,7 +19,7 @@ from src.full_node.sub_block_record import SubBlockRecord
|
|||
from src.types.sub_epoch_summary import SubEpochSummary
|
||||
from src.types.unfinished_block import UnfinishedBlock
|
||||
from src.util.errors import Err
|
||||
from src.util.ints import uint32, uint64, uint128
|
||||
from src.util.ints import uint32, uint64
|
||||
from src.consensus.find_fork_point import find_fork_point_in_chain
|
||||
from src.full_node.block_header_validation import (
|
||||
validate_finished_header_block,
|
||||
|
@ -296,9 +295,6 @@ class Blockchain:
|
|||
def get_next_difficulty(self, header_hash: bytes32, new_slot: bool) -> uint64:
|
||||
assert header_hash in self.sub_blocks
|
||||
curr = self.sub_blocks[header_hash]
|
||||
|
||||
ip_iters = calculate_ip_iters(self.constants, curr.ips, curr.required_iters)
|
||||
sp_iters = calculate_sp_iters(self.constants, curr.ips, curr.required_iters)
|
||||
return get_next_difficulty(
|
||||
self.constants,
|
||||
self.sub_blocks,
|
||||
|
@ -308,15 +304,12 @@ class Blockchain:
|
|||
uint64(curr.weight - self.sub_blocks[curr.prev_hash].weight),
|
||||
curr.deficit,
|
||||
new_slot,
|
||||
uint128(curr.total_iters - ip_iters + sp_iters),
|
||||
curr.sp_total_iters(self.constants),
|
||||
)
|
||||
|
||||
def get_next_slot_iters(self, header_hash: bytes32, new_slot: bool) -> uint64:
|
||||
assert header_hash in self.sub_blocks
|
||||
curr = self.sub_blocks[header_hash]
|
||||
|
||||
ip_iters = calculate_ip_iters(self.constants, curr.ips, curr.required_iters)
|
||||
sp_iters = calculate_sp_iters(self.constants, curr.ips, curr.required_iters)
|
||||
return (
|
||||
get_next_ips(
|
||||
self.constants,
|
||||
|
@ -327,7 +320,7 @@ class Blockchain:
|
|||
curr.ips,
|
||||
curr.deficit,
|
||||
new_slot,
|
||||
uint128(curr.total_iters - ip_iters + sp_iters),
|
||||
curr.sp_total_iters(self.constants),
|
||||
)
|
||||
* self.constants.SLOT_TIME_TARGET
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Union
|
||||
from typing import Dict, List, Union, Optional
|
||||
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.header_block import HeaderBlock
|
||||
|
@ -6,7 +6,6 @@ from src.types.unfinished_block import UnfinishedBlock
|
|||
from src.types.unfinished_header_block import UnfinishedHeaderBlock
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_sp_iters, calculate_ip_iters
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
from src.util.ints import uint32, uint64, uint128, uint8
|
||||
|
@ -91,13 +90,9 @@ def _get_last_block_in_previous_epoch(
|
|||
curr = fetched_blocks[fetched_index]
|
||||
fetched_index += 1
|
||||
|
||||
last_sb_ip_iters = calculate_ip_iters(constants, last_sb_in_slot.ips, last_sb_in_slot.required_iters)
|
||||
last_sb_sp_iters = calculate_sp_iters(constants, last_sb_in_slot.ips, last_sb_in_slot.required_iters)
|
||||
prev_signage_point_total_iters = last_sb_in_slot.total_iters - last_sb_ip_iters + last_sb_sp_iters
|
||||
|
||||
# Backtrack to find the last block before the signage point
|
||||
curr = sub_blocks[last_sb_in_slot.prev_hash]
|
||||
while curr.total_iters > prev_signage_point_total_iters or not curr.is_block:
|
||||
while curr.total_iters > last_sb_in_slot.sp_total_iters(constants) or not curr.is_block:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
return curr
|
||||
|
@ -109,7 +104,7 @@ def finishes_sub_epoch(
|
|||
deficit: uint8,
|
||||
also_finishes_epoch: bool,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
prev_header_hash: bytes32,
|
||||
prev_header_hash: Optional[bytes32],
|
||||
) -> bool:
|
||||
"""
|
||||
Returns true if the next sub-slot after height will form part of a new sub-epoch (or epoch if also_finished_epoch
|
||||
|
@ -119,6 +114,8 @@ def finishes_sub_epoch(
|
|||
if height < constants.SUB_EPOCH_SUB_BLOCKS - 1:
|
||||
return False
|
||||
|
||||
assert prev_header_hash is not None
|
||||
|
||||
# If last slot does not have enough blocks for a new challenge chain infusion, return same difficulty
|
||||
if deficit > 0:
|
||||
return False
|
||||
|
@ -192,6 +189,8 @@ def get_next_ips(
|
|||
(last_block_curr.total_iters - last_block_prev.total_iters)
|
||||
// (last_block_curr.timestamp - last_block_prev.timestamp)
|
||||
)
|
||||
new_ips_precise = new_ips_precise - (new_ips_precise % constants.NUM_SPS_SUB_SLOT) # Must divide the sub slot
|
||||
new_ips = uint64(truncate_to_significant_bits(new_ips_precise, constants.SIGNIFICANT_BITS))
|
||||
assert count_significant_bits(new_ips) <= constants.SIGNIFICANT_BITS
|
||||
|
||||
# Only change by a max factor as a sanity check
|
||||
|
@ -210,7 +209,7 @@ def get_next_ips(
|
|||
if new_ips >= last_block_curr.ips:
|
||||
return min(new_ips, max_ips)
|
||||
else:
|
||||
return max([uint64(1), new_ips, min_ips])
|
||||
return max([constants.NUM_SPS_SUB_SLOT, new_ips, min_ips])
|
||||
|
||||
|
||||
def get_next_difficulty(
|
||||
|
|
|
@ -36,7 +36,7 @@ def full_block_to_sub_block_record(
|
|||
len(block.finished_sub_slots) > 0,
|
||||
prev_sb.total_iters,
|
||||
)
|
||||
overflow = is_overflow_sub_block(constants, ips, required_iters)
|
||||
overflow = is_overflow_sub_block(constants, block.reward_chain_sub_block.signage_point_index)
|
||||
deficit = calculate_deficit(constants, block.height, prev_sb, overflow, len(block.finished_sub_slots) > 0)
|
||||
prev_block_hash = block.foliage_block.prev_block_hash if block.foliage_block is not None else None
|
||||
timestamp = block.foliage_block.timestamp if block.foliage_block is not None else None
|
||||
|
@ -69,7 +69,7 @@ def full_block_to_sub_block_record(
|
|||
constants,
|
||||
sub_blocks,
|
||||
block.height,
|
||||
prev_sb,
|
||||
sub_blocks[prev_sb.prev_hash],
|
||||
block.finished_sub_slots[0].challenge_chain.new_difficulty,
|
||||
block.finished_sub_slots[0].challenge_chain.new_ips,
|
||||
)
|
||||
|
@ -92,6 +92,7 @@ def full_block_to_sub_block_record(
|
|||
block.height,
|
||||
block.weight,
|
||||
block.total_iters,
|
||||
block.reward_chain_sub_block.signage_point_index,
|
||||
block.reward_chain_sub_block.challenge_chain_ip_vdf.output,
|
||||
icc_output,
|
||||
block.reward_chain_sub_block.get_hash(),
|
||||
|
@ -101,6 +102,7 @@ def full_block_to_sub_block_record(
|
|||
block.foliage_sub_block.foliage_sub_block_data.farmer_reward_puzzle_hash,
|
||||
required_iters,
|
||||
deficit,
|
||||
overflow,
|
||||
timestamp,
|
||||
prev_block_hash,
|
||||
finished_challenge_slot_hashes,
|
||||
|
|
|
@ -166,7 +166,12 @@ class FullNode:
|
|||
difficulty = self.blockchain.get_next_difficulty(peak, False)
|
||||
if peak is not None:
|
||||
ses: Optional[SubEpochSummary] = next_sub_epoch_summary(
|
||||
self.constants, self.blockchain.sub_blocks, self.blockchain.height_to_hash, peak.ips, peak_block
|
||||
self.constants,
|
||||
self.blockchain.sub_blocks,
|
||||
self.blockchain.height_to_hash,
|
||||
peak.signage_point_index,
|
||||
peak.ips,
|
||||
peak_block,
|
||||
)
|
||||
timelord_new_peak: timelord_protocol.NewPeak = timelord_protocol.NewPeak(
|
||||
peak_block.reward_chain_sub_block, difficulty, peak.deficit, peak.ips, ses
|
||||
|
@ -638,10 +643,10 @@ class FullNode:
|
|||
curr = await self.blockchain.get_full_block(curr.prev_header_hash)
|
||||
assert curr is not None
|
||||
peak_sub_slot = curr.finished_sub_slots[-1]
|
||||
peak_sub_slot_iters = new_peak.total_iters - calculate_ip_iters(
|
||||
self.constants, new_peak.ips, new_peak.required_iters
|
||||
peak_sub_slot_iters = new_peak.infusion_sub_slot_total_iters(self.constants)
|
||||
is_overflow: bool = is_overflow_sub_block(
|
||||
self.constants, sub_block.reward_chain_sub_block.signage_point_index
|
||||
)
|
||||
is_overflow: bool = is_overflow_sub_block(self.constants, new_peak.ips, new_peak.required_iters)
|
||||
if is_overflow:
|
||||
# Find the previous sub-slots end of slot
|
||||
if len(curr.finished_sub_slots) >= 2:
|
||||
|
@ -801,10 +806,7 @@ class FullNode:
|
|||
|
||||
peak: Optional[SubBlockRecord] = self.blockchain.get_peak()
|
||||
if peak is not None:
|
||||
peak_sp = calculate_sp_iters(self.constants, peak.ips, peak.required_iters)
|
||||
peak_ip_iters = calculate_ip_iters(self.constants, peak.ips, peak.required_iters)
|
||||
sp_iters = peak.total_iters - (peak_ip_iters - peak_sp)
|
||||
if block.total_iters < sp_iters:
|
||||
if block.total_iters < peak.sp_total_iters(self.constants):
|
||||
# This means this unfinished block is pretty far behind, it will not add weight to our chain
|
||||
return
|
||||
|
||||
|
@ -846,7 +848,12 @@ class FullNode:
|
|||
block.reward_chain_sp_proof,
|
||||
block.foliage_sub_block,
|
||||
next_sub_epoch_summary(
|
||||
self.constants, self.blockchain.sub_blocks, self.blockchain.height_to_hash, required_iters, block
|
||||
self.constants,
|
||||
self.blockchain.sub_blocks,
|
||||
self.blockchain.height_to_hash,
|
||||
block.reward_chain_sub_block.signage_point_index,
|
||||
required_iters,
|
||||
block,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1012,9 +1019,7 @@ class FullNode:
|
|||
# 3. In a future sub-slot that we already know of
|
||||
|
||||
# Checks that the proof of space is valid
|
||||
quality_string: Optional[bytes32] = request.proof_of_space.verify_and_get_quality_string(
|
||||
self.constants, request.challenge_chain_sp, request.challenge_chain_sp_signature
|
||||
)
|
||||
quality_string: Optional[bytes32] = request.proof_of_space.verify_and_get_quality_string(self.constants)
|
||||
assert len(quality_string) == 32
|
||||
|
||||
# Grab best transactions from Mempool for given tip target
|
||||
|
@ -1041,9 +1046,10 @@ class FullNode:
|
|||
quality_string,
|
||||
request.proof_of_space.size,
|
||||
difficulty,
|
||||
request.challenge_chain_sp,
|
||||
)
|
||||
sp_iters: uint64 = calculate_sp_iters(self.constants, ips, required_iters)
|
||||
ip_iters: uint64 = calculate_ip_iters(self.constants, ips, required_iters)
|
||||
sp_iters: uint64 = calculate_sp_iters(self.constants, ips, request.signage_point_index)
|
||||
ip_iters: uint64 = calculate_ip_iters(self.constants, ips, request.signage_point_index, required_iters)
|
||||
total_iters_pos_slot: uint128 = pos_sub_slot[2]
|
||||
|
||||
def get_plot_sig(to_sign, _) -> G2Element:
|
||||
|
@ -1060,6 +1066,7 @@ class FullNode:
|
|||
unfinished_block: Optional[UnfinishedBlock] = create_unfinished_block(
|
||||
self.constants,
|
||||
total_iters_pos_slot,
|
||||
request.signage_point_index,
|
||||
sp_iters,
|
||||
ip_iters,
|
||||
request.proof_of_space,
|
||||
|
@ -1068,13 +1075,13 @@ class FullNode:
|
|||
request.pool_target,
|
||||
get_plot_sig,
|
||||
get_plot_sig,
|
||||
sp_vdfs,
|
||||
uint64(int(time.time())),
|
||||
b"",
|
||||
spend_bundle,
|
||||
peak,
|
||||
self.blockchain.sub_blocks,
|
||||
finished_sub_slots,
|
||||
sp_vdfs,
|
||||
)
|
||||
self.full_node_store.add_candidate_block(quality_string, unfinished_block)
|
||||
|
||||
|
@ -1153,16 +1160,7 @@ class FullNode:
|
|||
ips, difficulty = get_ips_and_difficulty(
|
||||
self.constants, unfinished_block, self.blockchain.height_to_hash, prev_sb, self.blockchain.sub_blocks
|
||||
)
|
||||
q_str: Optional[bytes32] = unfinished_block.reward_chain_sub_block.proof_of_space.verify_and_get_quality_string(
|
||||
self.constants,
|
||||
)
|
||||
assert q_str is not None # We have previously validated this
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
q_str,
|
||||
unfinished_block.reward_chain_sub_block.proof_of_space.size,
|
||||
difficulty,
|
||||
)
|
||||
overflow = is_overflow_sub_block(self.constants, ips, required_iters)
|
||||
overflow = is_overflow_sub_block(self.constants, unfinished_block.reward_chain_sub_block.signage_point_index)
|
||||
if overflow:
|
||||
finished_sub_slots = self.full_node_store.get_finished_sub_slots(
|
||||
prev_sb,
|
||||
|
|
|
@ -247,7 +247,7 @@ class FullNodeStore:
|
|||
sub_slot_iters = calculate_sub_slot_iters(self.constants, peak.ips)
|
||||
|
||||
# If we don't have this slot, return False
|
||||
assert 0 < index < self.constants.NUM_CHECKPOINTS_PER_SLOT
|
||||
assert 0 < index < self.constants.NUM_SPS_SUB_SLOT
|
||||
for sub_slot, sp_dict, start_ss_total_iters in self.finished_sub_slots:
|
||||
if sub_slot is None and start_ss_total_iters == 0:
|
||||
ss_challenge_hash = self.constants.FIRST_CC_CHALLENGE
|
||||
|
@ -259,11 +259,11 @@ class FullNodeStore:
|
|||
# If we do have this slot, find the Prev sub-block from SP and validate SP
|
||||
if peak is not None and start_ss_total_iters > peak.total_iters:
|
||||
# We are in a future sub slot from the peak, so maybe there is a new IPS
|
||||
checkpoint_size: uint64 = uint64(next_sub_slot_iters // self.constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
checkpoint_size: uint64 = uint64(next_sub_slot_iters // self.constants.NUM_SPS_SUB_SLOT)
|
||||
delta_iters = checkpoint_size * index
|
||||
else:
|
||||
# We are not in a future sub slot from the peak, so there is no new IPS
|
||||
checkpoint_size: uint64 = uint64(sub_slot_iters // self.constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
checkpoint_size: uint64 = uint64(sub_slot_iters // self.constants.NUM_SPS_SUB_SLOT)
|
||||
delta_iters = checkpoint_size * index
|
||||
sp_total_iters = start_ss_total_iters + delta_iters
|
||||
|
||||
|
@ -330,7 +330,7 @@ class FullNodeStore:
|
|||
return SignagePoint(None, None, None, None)
|
||||
for _, sps_at_index in sps.items():
|
||||
for sp in sps_at_index:
|
||||
if sp.cc_vdf.get_hash() == cc_signage_point:
|
||||
if sp.cc_vdf.output.get_hash() == cc_signage_point:
|
||||
return sp
|
||||
return None
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ def make_sub_epoch_summary(
|
|||
constants: ConsensusConstants,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
blocks_included_height: uint32,
|
||||
prev_sb: SubBlockRecord,
|
||||
prev_prev_sub_block: bytes32,
|
||||
new_difficulty: Optional[uint64],
|
||||
new_ips: Optional[uint64],
|
||||
) -> SubEpochSummary:
|
||||
|
@ -25,11 +25,11 @@ def make_sub_epoch_summary(
|
|||
"blocks_included_height". Prev_sb is the last sub block in the previous sub-epoch. On a new epoch,
|
||||
new_difficulty and new_ips are also added.
|
||||
"""
|
||||
assert prev_sb.height == blocks_included_height - 1
|
||||
assert prev_prev_sub_block.height == blocks_included_height - 2
|
||||
if blocks_included_height // constants.SUB_EPOCH_SUB_BLOCKS == 1:
|
||||
ses = SubEpochSummary(constants.GENESIS_SES_HASH, constants.FIRST_RC_CHALLENGE, uint8(0), None, None)
|
||||
else:
|
||||
curr = prev_sb
|
||||
curr = prev_prev_sub_block
|
||||
while curr.sub_epoch_summary_included is None:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
assert curr.sub_epoch_summary_included is not None
|
||||
|
@ -37,7 +37,7 @@ def make_sub_epoch_summary(
|
|||
ses = SubEpochSummary(
|
||||
prev_ses,
|
||||
curr.finished_reward_slot_hashes[-1],
|
||||
curr.height % constants.SUB_EPOCH_SUB_BLOCKS,
|
||||
uint8(curr.height % constants.SUB_EPOCH_SUB_BLOCKS),
|
||||
new_difficulty,
|
||||
new_ips,
|
||||
)
|
||||
|
@ -49,6 +49,7 @@ def next_sub_epoch_summary(
|
|||
constants: ConsensusConstants,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
signage_point_index: uint8,
|
||||
required_iters: uint64,
|
||||
block: Union[UnfinishedBlock, FullBlock],
|
||||
) -> Optional[SubEpochSummary]:
|
||||
|
@ -57,8 +58,6 @@ def next_sub_epoch_summary(
|
|||
ips = constants.IPS_STARTING
|
||||
else:
|
||||
assert prev_sb is not None
|
||||
prev_ip_iters = calculate_ip_iters(constants, prev_sb.ips, prev_sb.required_iters)
|
||||
prev_sp_iters = calculate_sp_iters(constants, prev_sb.ips, prev_sb.required_iters)
|
||||
ips = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
|
@ -68,18 +67,22 @@ def next_sub_epoch_summary(
|
|||
prev_sb.ips,
|
||||
prev_sb.deficit,
|
||||
len(block.finished_sub_slots) > 0,
|
||||
uint128(block.total_iters - prev_ip_iters + prev_sp_iters),
|
||||
prev_sb.sp_total_iters(constants),
|
||||
)
|
||||
overflow = is_overflow_sub_block(constants, ips, required_iters)
|
||||
overflow = is_overflow_sub_block(constants, signage_point_index)
|
||||
deficit = calculate_deficit(constants, block.height, prev_sb, overflow, len(block.finished_sub_slots) > 0)
|
||||
finishes_se = finishes_sub_epoch(constants, block.height, deficit, False, sub_blocks, prev_sb.header_hash)
|
||||
finishes_epoch: bool = finishes_sub_epoch(constants, block.height, deficit, True, sub_blocks, prev_sb.header_hash)
|
||||
finishes_se = finishes_sub_epoch(
|
||||
constants, block.height, deficit, False, sub_blocks, prev_sb.header_hash if prev_sb is not None else None
|
||||
)
|
||||
finishes_epoch: bool = finishes_sub_epoch(
|
||||
constants, block.height, deficit, True, sub_blocks, prev_sb.header_hash if prev_sb is not None else None
|
||||
)
|
||||
|
||||
if finishes_se:
|
||||
assert prev_sb is not None
|
||||
if finishes_epoch:
|
||||
ip_iters = calculate_ip_iters(constants, ips, required_iters)
|
||||
sp_iters = calculate_sp_iters(constants, ips, required_iters)
|
||||
sp_iters = calculate_sp_iters(constants, ips, signage_point_index)
|
||||
ip_iters = calculate_ip_iters(constants, ips, signage_point_index, required_iters)
|
||||
next_difficulty = get_next_difficulty(
|
||||
constants,
|
||||
sub_blocks,
|
||||
|
@ -105,5 +108,5 @@ def next_sub_epoch_summary(
|
|||
else:
|
||||
next_difficulty = None
|
||||
next_ips = None
|
||||
return make_sub_epoch_summary(constants, sub_blocks, block.height, prev_sb, next_difficulty, next_ips)
|
||||
return make_sub_epoch_summary(constants, sub_blocks, block.height + 1, prev_sb, next_difficulty, next_ips)
|
||||
return None
|
||||
|
|
|
@ -2,7 +2,7 @@ from dataclasses import dataclass
|
|||
from typing import Optional, List
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.types.header_block import HeaderBlock
|
||||
from src.consensus.pot_iterations import calculate_sp_iters, calculate_ip_iters, calculate_sub_slot_iters
|
||||
from src.types.sub_epoch_summary import SubEpochSummary
|
||||
from src.util.ints import uint8, uint32, uint64, uint128
|
||||
from src.types.sized_bytes import bytes32
|
||||
|
@ -24,6 +24,7 @@ class SubBlockRecord(Streamable):
|
|||
sub_block_height: uint32
|
||||
weight: uint128 # Total cumulative difficulty of all ancestor blocks since genesis
|
||||
total_iters: uint128 # Total number of VDF iterations since genesis, including this sub-block
|
||||
signage_point_index: uint8
|
||||
challenge_vdf_output: ClassgroupElement # This is the intermediary VDF output at ip_iters in challenge chain
|
||||
infused_challenge_vdf_output: Optional[
|
||||
ClassgroupElement
|
||||
|
@ -35,6 +36,7 @@ class SubBlockRecord(Streamable):
|
|||
farmer_puzzle_hash: bytes32
|
||||
required_iters: uint64 # The number of iters required for this proof of space
|
||||
deficit: uint8 # A deficit of 5 is an overflow block after an infusion. Deficit of 4 is a challenge block
|
||||
overflow: bool
|
||||
|
||||
# Block (present iff is_block)
|
||||
timestamp: Optional[uint64]
|
||||
|
@ -49,16 +51,34 @@ class SubBlockRecord(Streamable):
|
|||
sub_epoch_summary_included: Optional[SubEpochSummary]
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
def height(self) -> uint32:
|
||||
return self.sub_block_height
|
||||
|
||||
@property
|
||||
def is_block(self):
|
||||
def is_block(self) -> bool:
|
||||
return self.timestamp is not None
|
||||
|
||||
@property
|
||||
def first_in_sub_slot(self):
|
||||
def first_in_sub_slot(self) -> bool:
|
||||
return self.finished_challenge_slot_hashes is not None
|
||||
|
||||
def is_challenge_sub_block(self, constants: ConsensusConstants):
|
||||
def is_challenge_sub_block(self, constants: ConsensusConstants) -> bool:
|
||||
return self.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1
|
||||
|
||||
def pos_sub_slot_total_iters(self, constants: ConsensusConstants) -> uint128:
|
||||
if self.overflow:
|
||||
return uint128(self.total_iters - self.ip_iters(constants) - calculate_sub_slot_iters(constants, self.ips))
|
||||
else:
|
||||
return uint128(self.total_iters - self.ip_iters(constants))
|
||||
|
||||
def infusion_sub_slot_total_iters(self, constants: ConsensusConstants) -> uint128:
|
||||
return self.total_iters - self.ip_iters(constants)
|
||||
|
||||
def sp_iters(self, constants: ConsensusConstants) -> uint64:
|
||||
return calculate_sp_iters(constants, self.ips, self.signage_point_index)
|
||||
|
||||
def ip_iters(self, constants: ConsensusConstants) -> uint64:
|
||||
return calculate_ip_iters(constants, self.ips, self.signage_point_index, self.required_iters)
|
||||
|
||||
def sp_total_iters(self, constants: ConsensusConstants):
|
||||
return self.pos_sub_slot_total_iters(constants) + self.sp_iters(constants)
|
||||
|
|
|
@ -11,7 +11,7 @@ from blspy import G1Element, G2Element, AugSchemeMPL
|
|||
|
||||
from chiapos import DiskProver
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_iterations_quality
|
||||
from src.consensus.pot_iterations import calculate_iterations_quality, calculate_sp_interval_iters
|
||||
from src.protocols import harvester_protocol
|
||||
from src.server.connection import PeerConnections
|
||||
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
|
||||
|
@ -36,7 +36,8 @@ class Harvester:
|
|||
no_key_filenames: Set[Path]
|
||||
farmer_public_keys: List[G1Element]
|
||||
pool_public_keys: List[G1Element]
|
||||
cached_challenges: List[harvester_protocol.NewChallenge]
|
||||
cached_signage_points: List[harvester_protocol.NewSignagePoint]
|
||||
cached_qualities: Dict[bytes32, bytes32]
|
||||
root_path: Path
|
||||
_is_shutdown: bool
|
||||
executor: ThreadPoolExecutor
|
||||
|
@ -61,7 +62,8 @@ class Harvester:
|
|||
self.state_changed_callback = None
|
||||
self.server = None
|
||||
self.constants = constants
|
||||
self.cached_challenges = []
|
||||
self.cached_signage_points = []
|
||||
self.cached_qualities = {}
|
||||
|
||||
async def _start(self):
|
||||
self._refresh_lock = asyncio.Lock()
|
||||
|
@ -169,27 +171,29 @@ class Harvester:
|
|||
log.warning("Not farming any plots on this harvester. Check your configuration.")
|
||||
return
|
||||
|
||||
for new_challenge in self.cached_challenges:
|
||||
async for msg in self.new_challenge(new_challenge):
|
||||
for new_challenge in self.cached_signage_points:
|
||||
async for msg in self.new_signage_point(new_challenge):
|
||||
yield msg
|
||||
self.cached_challenges = []
|
||||
self.cached_signage_points = []
|
||||
self._state_changed("plots")
|
||||
|
||||
@api_request
|
||||
async def new_challenge(self, new_challenge: harvester_protocol.NewChallenge):
|
||||
async def new_signage_point(self, new_challenge: harvester_protocol.NewSignagePoint):
|
||||
"""
|
||||
The harvester receives a new challenge from the farmer, this happens at the start of each slot.
|
||||
The harvester receives a new signage point from the farmer, this happens at the start of each slot.
|
||||
The harvester does a few things:
|
||||
1. Checks the plot filter to see which plots can qualify for this slot (1/16 on average)
|
||||
2. Looks up the quality for these plots, approximately 7 reads per plot which qualifies. Note that each plot
|
||||
may have 0, 1, 2, etc qualities for that challenge: but on average it will have 1.
|
||||
3. Checks the required_iters for each quality to see which are eligible for inclusion (< sub_slot_iters)
|
||||
4. Looks up the full proof of space in the plot for each quality, approximately 64 reads per quality
|
||||
5. Returns the proof of space to the farmer
|
||||
1. If it's the first time seeing this challenge, clears the old challenges, and gets the qualities for each plot
|
||||
This is approximately 7 reads per plot which qualifies. Note that each plot
|
||||
may have 0, 1, 2, etc qualities for that challenge: but on average it will have 1. If it's not the first time,
|
||||
the qualities are fetched from the cache.
|
||||
2. Checks the required_iters for each quality and the given signage point, to see which are eligible for
|
||||
inclusion (required_iters < sp_interval_iters).
|
||||
3. Looks up the full proof of space in the plot for each quality, approximately 64 reads per quality
|
||||
4. Returns the proof of space to the farmer
|
||||
"""
|
||||
if len(self.pool_public_keys) == 0 or len(self.farmer_public_keys) == 0:
|
||||
self.cached_challenges = self.cached_challenges[:5]
|
||||
self.cached_challenges.insert(0, new_challenge)
|
||||
self.cached_signage_points = self.cached_signage_points[:5]
|
||||
self.cached_signage_points.insert(0, new_challenge)
|
||||
return
|
||||
|
||||
start = time.time()
|
||||
|
@ -200,26 +204,36 @@ class Harvester:
|
|||
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
# Remove old things from cache
|
||||
if len(self.cached_qualities) != 0:
|
||||
for key in self.cached_qualities.keys():
|
||||
if key != new_challenge.challenge_hash:
|
||||
del self.cached_qualities[key]
|
||||
|
||||
lookup_qualities = new_challenge.challenge_hash in self.cached_qualities
|
||||
|
||||
def blocking_lookup(filename: Path, plot_info: PlotInfo, prover: DiskProver) -> List[ProofOfSpace]:
|
||||
# Uses the DiskProver object to lookup qualities. This is a blocking call,
|
||||
# so it should be run in a thread pool.
|
||||
try:
|
||||
quality_strings = prover.get_qualities_for_challenge(new_challenge.challenge_hash)
|
||||
except Exception:
|
||||
log.error("Error using prover object. Reinitializing prover object.")
|
||||
self.provers[filename] = dataclasses.replace(plot_info, prover=DiskProver(str(filename)))
|
||||
return []
|
||||
if lookup_qualities:
|
||||
try:
|
||||
quality_strings = prover.get_qualities_for_challenge(new_challenge.challenge_hash)
|
||||
except Exception:
|
||||
log.error("Error using prover object. Reinitializing prover object.")
|
||||
self.provers[filename] = dataclasses.replace(plot_info, prover=DiskProver(str(filename)))
|
||||
return []
|
||||
else:
|
||||
quality_strings = self.cached_qualities[new_challenge.challenge_hash]
|
||||
|
||||
responses: List[ProofOfSpace] = []
|
||||
if quality_strings is not None:
|
||||
# Found proofs of space (on average 1 is expected per plot)
|
||||
for index, quality_str in enumerate(quality_strings):
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
quality_str,
|
||||
prover.get_size(),
|
||||
new_challenge.difficulty,
|
||||
quality_str, prover.get_size(), new_challenge.difficulty, new_challenge.challenge_hash
|
||||
)
|
||||
if required_iters < new_challenge.slot_iterations:
|
||||
sp_interval_iters = calculate_sp_interval_iters(self.constants, new_challenge.ips)
|
||||
if required_iters < sp_interval_iters:
|
||||
# Found a very good proof of space! will fetch the whole proof from disk, then send to farmer
|
||||
try:
|
||||
proof_xs = prover.get_full_proof(new_challenge.challenge_hash, index)
|
||||
|
@ -234,6 +248,7 @@ class Harvester:
|
|||
ProofOfSpace(
|
||||
new_challenge.challenge_hash,
|
||||
plot_info.pool_public_key,
|
||||
None,
|
||||
plot_public_key,
|
||||
uint8(prover.get_size()),
|
||||
proof_xs,
|
||||
|
@ -241,15 +256,17 @@ class Harvester:
|
|||
)
|
||||
return responses
|
||||
|
||||
async def lookup_challenge(filename: Path, prover: DiskProver) -> List[harvester_protocol.ChallengeResponse]:
|
||||
async def lookup_challenge(filename: Path, prover: DiskProver) -> List[harvester_protocol.NewProofOfSpace]:
|
||||
# Executes a DiskProverLookup in a thread pool, and returns responses
|
||||
all_responses: List[harvester_protocol.ChallengeResponse] = []
|
||||
all_responses: List[harvester_protocol.NewProofOfSpace] = []
|
||||
proofs_of_space: List[ProofOfSpace] = await loop.run_in_executor(
|
||||
self.executor, blocking_lookup, filename, prover
|
||||
)
|
||||
for proof_of_space in proofs_of_space:
|
||||
all_responses.append(
|
||||
harvester_protocol.ChallengeResponse(prover.get_id(), new_challenge.challenge_hash, proof_of_space)
|
||||
harvester_protocol.NewProofOfSpace(
|
||||
prover.get_id(), proof_of_space, new_challenge.signage_point_index
|
||||
)
|
||||
)
|
||||
return all_responses
|
||||
|
||||
|
@ -258,7 +275,7 @@ class Harvester:
|
|||
if filename.exists():
|
||||
# Passes the plot filter (does not check sp filter yet though, since we have not reached sp)
|
||||
# This is being executed at the beginning of the slot
|
||||
if ProofOfSpace.can_create_proof(
|
||||
if ProofOfSpace.passes_plot_filter(
|
||||
self.constants,
|
||||
plot_info.prover.get_id(),
|
||||
new_challenge.challenge_hash,
|
||||
|
|
|
@ -20,14 +20,15 @@ class NewSignagePoint:
|
|||
challenge_chain_sp: bytes32
|
||||
reward_chain_sp: bytes32
|
||||
difficulty: uint64
|
||||
ips: uint64
|
||||
signage_point_index: uint8
|
||||
slot_iterations: uint64
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class DeclareProofOfSpace:
|
||||
challenge_chain_sp: bytes32
|
||||
signage_point_index: uint8
|
||||
reward_chain_sp: bytes32
|
||||
proof_of_space: ProofOfSpace
|
||||
challenge_chain_sp_signature: G2Element
|
||||
|
|
|
@ -6,7 +6,7 @@ from blspy import G1Element, G2Element
|
|||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.cbor_message import cbor_message
|
||||
from src.util.ints import uint64
|
||||
from src.util.ints import uint64, uint8
|
||||
|
||||
"""
|
||||
Protocol between harvester and farmer.
|
||||
|
@ -22,17 +22,20 @@ class HarvesterHandshake:
|
|||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class NewChallenge:
|
||||
class NewSignagePoint:
|
||||
challenge_hash: bytes32
|
||||
difficulty: uint64
|
||||
slot_iterations: uint64
|
||||
ips: uint64
|
||||
signage_point_index: uint8
|
||||
sp_hash: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class ChallengeResponse:
|
||||
class NewProofOfSpace:
|
||||
plot_identifier: str
|
||||
proof: ProofOfSpace
|
||||
signage_point_index: uint8
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
|
@ -4,6 +4,7 @@ import logging
|
|||
import time
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from blspy import G2Element
|
||||
from chiavdf import create_discriminant
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
|
@ -18,7 +19,7 @@ from src.server.outbound_message import OutboundMessage, NodeType, Message, Deli
|
|||
from src.server.server import ChiaServer
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
from src.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.types.reward_chain_sub_block import RewardChainSubBlock
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.types.slots import ChallengeChainSubSlot, InfusedChallengeChainSubSlot, RewardChainSubSlot, SubSlotProofs
|
||||
from src.types.vdf import VDFInfo, VDFProof
|
||||
|
@ -28,16 +29,27 @@ from src.util.ints import uint64, uint8, int512
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def iters_from_proof_of_space(constants, pos: ProofOfSpace, ips, difficulty) -> Tuple[uint64, uint64]:
|
||||
quality = pos.verify_and_get_quality_string()
|
||||
def iters_from_sub_block(
|
||||
constants,
|
||||
reward_chain_sub_block: RewardChainSubBlock,
|
||||
ips: uint64,
|
||||
difficulty: uint64,
|
||||
) -> Tuple[uint64, uint64]:
|
||||
quality = reward_chain_sub_block.proof_of_space.verify_and_get_quality_string()
|
||||
if reward_chain_sub_block.challenge_chain_sp_vdf is None:
|
||||
assert reward_chain_sub_block.signage_point_index == 0
|
||||
cc_sp: bytes32 = reward_chain_sub_block.proof_of_space.challenge_hash
|
||||
else:
|
||||
cc_sp: bytes32 = reward_chain_sub_block.challenge_chain_sp_vdf.get_hash()
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality,
|
||||
pos.size,
|
||||
reward_chain_sub_block.proof_of_space.size,
|
||||
difficulty,
|
||||
cc_sp,
|
||||
)
|
||||
return (
|
||||
calculate_sp_iters(constants, ips, required_iters),
|
||||
calculate_ip_iters(constants, ips, required_iters),
|
||||
calculate_sp_iters(constants, reward_chain_sub_block.signage_point_index),
|
||||
calculate_ip_iters(constants, ips, reward_chain_sub_block.signage_point_index, required_iters),
|
||||
)
|
||||
|
||||
|
||||
|
@ -52,6 +64,7 @@ class Timelord:
|
|||
self.ip_whitelist = self.config["vdf_clients"]["ip"]
|
||||
self.server: Optional[ChiaServer] = None
|
||||
self.chain_type_to_stream: Dict[str, Tuple[ip, asyncio.StreamReader, asyncio.StreamWriter]] = {}
|
||||
|
||||
# Chains that currently don't have a vdf_client.
|
||||
self.unspawned_chains: List[str] = ["cc", "rc", "icc"]
|
||||
# Chains that currently accept iterations.
|
||||
|
@ -71,7 +84,7 @@ class Timelord:
|
|||
# For each iteration submitted, know if it's a signage point, an infusion point or an end of slot.
|
||||
self.iteration_to_proof_type: Dict[uint64, str] = {}
|
||||
# List of proofs finished.
|
||||
self.proofs_finished: List[Tuple[str, VDFInfo, VDFProof]] = {}
|
||||
self.proofs_finished: List[Tuple[str, VDFInfo, VDFProof]] = []
|
||||
# Data to send at vdf_client initialization.
|
||||
self.discriminant_to_submit: Dict[str, bytes32] = {}
|
||||
self.initial_form_to_submit: Dict[str, ClassgroupElement] = {}
|
||||
|
@ -79,7 +92,7 @@ class Timelord:
|
|||
self.new_subslot = False
|
||||
self.finished_sp = 0
|
||||
self.cached_peak = None
|
||||
self.overflow_blocks: List[Tuple[timelord_protocol.NewUnfinishedSubBlock]] = []
|
||||
self.overflow_blocks: List[timelord_protocol.NewUnfinishedSubBlock] = []
|
||||
|
||||
def _set_server(self, server: ChiaServer):
|
||||
self.server = server
|
||||
|
@ -93,8 +106,8 @@ class Timelord:
|
|||
@api_request
|
||||
async def new_unfinished_subblock(self, new_unfinished_subblock: timelord_protocol.NewUnfinishedSubBlock):
|
||||
async with self.lock:
|
||||
sp_iters, ip_iters = iters_from_proof_of_space(
|
||||
new_unfinished_subblock.reward_chain_sub_block.proof_of_space,
|
||||
sp_iters, ip_iters = iters_from_sub_block(
|
||||
new_unfinished_subblock.reward_chain_sub_block,
|
||||
self.cached_peak.ips,
|
||||
self.cached_peak.difficulty,
|
||||
)
|
||||
|
@ -103,7 +116,7 @@ class Timelord:
|
|||
elif ip_iters > self.last_ip_iters:
|
||||
self.unfinished_blocks.append(new_unfinished_subblock)
|
||||
for chain in ["cc", "icc", "rc"]:
|
||||
self.iters_to_submit[chain] = uint64(ip_iters - self.last_ip_iters)
|
||||
self.iters_to_submit[chain].append(uint64(ip_iters - self.last_ip_iters))
|
||||
self.iteration_to_proof_type[ip_iters - self.last_ip_iters] = "ip"
|
||||
|
||||
async def _handle_client(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
|
||||
|
@ -151,8 +164,11 @@ class Timelord:
|
|||
self.initial_form_to_submit["rc"] = ClassgroupElement.get_default_element()
|
||||
# Retrieve the iterations of this peak.
|
||||
if not self.new_subslot:
|
||||
_, ip_iters = iters_from_proof_of_space(
|
||||
self.constants, self.sub_block.proof_of_space, self.last_peak.ips, self.last_peak.difficulty
|
||||
_, ip_iters = iters_from_sub_block(
|
||||
self.constants,
|
||||
self.last_peak.reward_chain_sub_block.proof_of_space,
|
||||
self.last_peak.ips,
|
||||
self.last_peak.difficulty,
|
||||
)
|
||||
else:
|
||||
ip_iters = 0
|
||||
|
@ -160,16 +176,16 @@ class Timelord:
|
|||
|
||||
sub_slot_iters = calculate_sub_slot_iters(self.constants, self.last_peak.ips)
|
||||
# Adjust all signage points iterations to the peak.
|
||||
iters_per_signage = uint64(sub_slot_iters // self.constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
iters_per_signage = uint64(sub_slot_iters // self.constants.NUM_SPS_SUB_SLOT)
|
||||
self.signage_point_iters = [
|
||||
k * iters_per_signage - ip_iters
|
||||
for k in range(1, self.constants.NUM_CHECKPOINTS_PER_SLOT + 1)
|
||||
for k in range(1, self.constants.NUM_SPS_SUB_SLOT + 1)
|
||||
if k * iters_per_signage - ip_iters > 0 and k * iters_per_signage <= sub_slot_iters
|
||||
]
|
||||
# Adjust all unfinished blocks iterations to the peak.
|
||||
new_unfinished_blocks = []
|
||||
for block in self.unfinished_blocks:
|
||||
block_sp_iters, block_ip_iters = iters_from_proof_of_space(
|
||||
block_sp_iters, block_ip_iters = iters_from_sub_block(
|
||||
self.constants, block.proof_of_space, self.last_peak.ips, self.last_peak.difficulty
|
||||
)
|
||||
if block_sp_iters < block_ip_iters:
|
||||
|
@ -184,7 +200,7 @@ class Timelord:
|
|||
self.iteration_to_proof_type[new_block_iters] = "ip"
|
||||
new_overflow_blocks = []
|
||||
for block in self.overflow_blocks:
|
||||
_, block_ip_iters = iters_from_proof_of_space(
|
||||
_, block_ip_iters = iters_from_sub_block(
|
||||
self.constants, block.proof_of_space, self.last_peak.ips, self.last_peak.difficulty
|
||||
)
|
||||
new_block_iters = block_ip_iters - ip_iters
|
||||
|
@ -294,7 +310,7 @@ class Timelord:
|
|||
Delivery.BROADCAST,
|
||||
)
|
||||
)
|
||||
# Cleanup the signage point fromm memory.
|
||||
# Cleanup the signage point from memory.
|
||||
self.signage_point_iters.remove(signage_iters[0])
|
||||
for chain in ["cc", "rc"]:
|
||||
del self.iteration_to_proof_type[chain][signage_iters[0]]
|
||||
|
@ -302,33 +318,35 @@ class Timelord:
|
|||
self.proofs_finished = self._clear_proof_list(signage_iters[0])
|
||||
# Send the next signage point to the chains.
|
||||
if len(self.signage_point_iters) > 0:
|
||||
next_sp = min(self.ignage_point_iters)
|
||||
next_sp = min(self.signage_point_iters)
|
||||
for chain in ["cc", "rc"]:
|
||||
self.iters_to_submit[chain] = next_sp
|
||||
self.iteration_to_proof_type[next_sp] = "sp"
|
||||
|
||||
async def _check_for_new_ip(self):
|
||||
infusion_iters = [iter for iter, t in self.iteration_to_proof_type if t == "ip"]
|
||||
for iter in infusion_iters:
|
||||
for iteration in infusion_iters:
|
||||
proofs_with_iter = [
|
||||
(chain, info, proof) for chain, info, proof in self.proofs_finished if info.number_of_iterations == iter
|
||||
(chain, info, proof)
|
||||
for chain, info, proof in self.proofs_finished
|
||||
if info.number_of_iterations == iteration
|
||||
]
|
||||
chain_count = 3 if self.has_icc else 2
|
||||
if len(proofs_with_iter) == chain_count:
|
||||
block = None
|
||||
for unfinished_block in self.unfinished_blocks + self.overflow_blocks:
|
||||
_, ip_iters = iters_from_proof_of_space(
|
||||
_, ip_iters = iters_from_sub_block(
|
||||
self.constants, self.cached_peak.ips, self.cached_peak.difficulty
|
||||
)
|
||||
if ip_iters - self.last_ip_iters == iter:
|
||||
if ip_iters - self.last_ip_iters == iteration:
|
||||
block = unfinished_block
|
||||
break
|
||||
if block is not None:
|
||||
self.unfinished_blocks.remove(block)
|
||||
for chain in ["cc", "rc"]:
|
||||
del self.iteration_to_proof_type[chain][iter]
|
||||
del self.iteration_to_proof_type[chain][iteration]
|
||||
if self.has_icc:
|
||||
del self.iteration_to_proof_type["icc"][iter]
|
||||
del self.iteration_to_proof_type["icc"][iteration]
|
||||
challenge_hash = block.reward_chain_sub_block.get_hash()
|
||||
icc_info = None
|
||||
icc_proof = None
|
||||
|
@ -358,8 +376,8 @@ class Timelord:
|
|||
Delivery.BROADCAST,
|
||||
)
|
||||
)
|
||||
for iter in infusion_iters:
|
||||
self.proofs_finished = self._clear_proof_list(iter)
|
||||
for iteration in infusion_iters:
|
||||
self.proofs_finished = self._clear_proof_list(iteration)
|
||||
|
||||
async def _check_for_end_of_subslot(self):
|
||||
chains_finished = [
|
||||
|
@ -424,7 +442,7 @@ class Timelord:
|
|||
# Calculate overflow blocks for the next subslot.
|
||||
self.overflow_blocks = []
|
||||
for unfinished_block in self.unfinished_blocks:
|
||||
sp_iters, ip_iters = iters_from_proof_of_space(
|
||||
sp_iters, ip_iters = iters_from_sub_block(
|
||||
self.constants, self.cached_peak.ips, self.cached_peak.difficulty
|
||||
)
|
||||
if sp_iters > ip_iters:
|
||||
|
|
|
@ -5,8 +5,10 @@ from bitstring import BitArray
|
|||
from blspy import G1Element, G2Element
|
||||
|
||||
from chiapos import Verifier
|
||||
|
||||
from src.consensus.pos_quality import quality_str_to_quality
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint8
|
||||
from src.util.ints import uint8, uint64
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from src.util.hash import std_hash
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
|
@ -28,16 +30,10 @@ class ProofOfSpace(Streamable):
|
|||
return self.calculate_plot_id_ph(self.pool_contract_puzzle_hash, self.plot_public_key)
|
||||
return self.calculate_plot_id_pk(self.pool_public_key, self.plot_public_key)
|
||||
|
||||
def verify_and_get_quality_string(
|
||||
self,
|
||||
constants: ConsensusConstants,
|
||||
sp_output_hash: Optional[bytes32] = None,
|
||||
sp_signature: Optional[G2Element] = None,
|
||||
) -> Optional[bytes32]:
|
||||
def verify_and_get_quality_string(self, constants: ConsensusConstants) -> Optional[bytes32]:
|
||||
v: Verifier = Verifier()
|
||||
plot_id: bytes32 = self.get_plot_id()
|
||||
|
||||
if not self.can_create_proof(constants, plot_id, self.challenge_hash, sp_output_hash, sp_signature):
|
||||
if not ProofOfSpace.passes_plot_filter(constants, plot_id, self.challenge_hash):
|
||||
return None
|
||||
|
||||
quality_str = v.validate_proof(plot_id, self.size, self.challenge_hash, bytes(self.proof))
|
||||
|
@ -48,23 +44,9 @@ class ProofOfSpace(Streamable):
|
|||
return quality_str
|
||||
|
||||
@staticmethod
|
||||
def can_create_proof(
|
||||
constants: ConsensusConstants,
|
||||
plot_id: bytes32,
|
||||
challenge_hash: bytes32,
|
||||
sp_output_hash: Optional[bytes32] = None,
|
||||
sp_signature: Optional[G2Element] = None,
|
||||
) -> bool:
|
||||
# If sp_output is provided, both plot filters are checked. Otherwise only the first one is checked
|
||||
def passes_plot_filter(constants: ConsensusConstants, plot_id: bytes32, challenge_hash: bytes32) -> bool:
|
||||
plot_filter = BitArray(std_hash(plot_id + challenge_hash))
|
||||
if sp_output_hash is None:
|
||||
return plot_filter[: constants.NUMBER_ZERO_BITS_PLOT_FILTER].uint == 0
|
||||
else:
|
||||
sp_filter = BitArray(std_hash(plot_id + sp_output_hash + bytes(sp_signature)))
|
||||
return (
|
||||
plot_filter[: constants.NUMBER_ZERO_BITS_PLOT_FILTER].uint == 0
|
||||
and sp_filter[: constants.NUMBER_ZERO_BITS_SP_FILTER].uint == 0
|
||||
)
|
||||
return plot_filter[: constants.NUMBER_ZERO_BITS_PLOT_FILTER].uint == 0
|
||||
|
||||
@staticmethod
|
||||
def calculate_plot_id_pk(
|
||||
|
|
|
@ -2,7 +2,7 @@ from dataclasses import dataclass
|
|||
from blspy import G2Element
|
||||
from typing import Optional
|
||||
|
||||
from src.util.ints import uint32, uint128
|
||||
from src.util.ints import uint32, uint128, uint8
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.types.vdf import VDFInfo
|
||||
|
@ -12,6 +12,7 @@ from src.types.vdf import VDFInfo
|
|||
@streamable
|
||||
class RewardChainSubBlockUnfinished(Streamable):
|
||||
total_iters: uint128
|
||||
signage_point_index: uint8
|
||||
proof_of_space: ProofOfSpace
|
||||
challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
||||
challenge_chain_sp_signature: G2Element
|
||||
|
@ -25,6 +26,7 @@ class RewardChainSubBlock(Streamable):
|
|||
weight: uint128
|
||||
sub_block_height: uint32
|
||||
total_iters: uint128
|
||||
signage_point_index: uint8
|
||||
proof_of_space: ProofOfSpace
|
||||
challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
||||
challenge_chain_sp_signature: G2Element
|
||||
|
@ -38,6 +40,7 @@ class RewardChainSubBlock(Streamable):
|
|||
def get_unfinished(self) -> RewardChainSubBlockUnfinished:
|
||||
return RewardChainSubBlockUnfinished(
|
||||
self.total_iters,
|
||||
self.signage_point_index,
|
||||
self.proof_of_space,
|
||||
self.challenge_chain_sp_vdf,
|
||||
self.challenge_chain_sp_signature,
|
||||
|
|
|
@ -23,14 +23,12 @@ from src.consensus.pot_iterations import (
|
|||
calculate_iterations_quality,
|
||||
calculate_sp_iters,
|
||||
calculate_sub_slot_iters,
|
||||
)
|
||||
from src.full_node.difficulty_adjustment import (
|
||||
get_next_difficulty,
|
||||
get_next_ips,
|
||||
finishes_sub_epoch,
|
||||
calculate_sp_interval_iters,
|
||||
is_overflow_sub_block,
|
||||
)
|
||||
from src.full_node.full_block_to_sub_block_record import full_block_to_sub_block_record
|
||||
from src.full_node.make_sub_epoch_summary import make_sub_epoch_summary
|
||||
from src.full_node.make_sub_epoch_summary import next_sub_epoch_summary
|
||||
from src.full_node.signage_point import SignagePoint
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
from src.plotting.plot_tools import load_plots, PlotInfo
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
|
@ -231,6 +229,7 @@ class BlockTools:
|
|||
height_to_hash, difficulty, sub_blocks = load_block_list(block_list, constants)
|
||||
|
||||
latest_sub_block: SubBlockRecord = sub_blocks[block_list[-1].header_hash]
|
||||
print(f"Last sub block {latest_sub_block.height} {latest_sub_block.total_iters}")
|
||||
curr = latest_sub_block
|
||||
while not curr.is_block:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
@ -238,25 +237,23 @@ class BlockTools:
|
|||
start_height = curr.height
|
||||
|
||||
curr = latest_sub_block
|
||||
sub_blocks_added_this_sub_slot = 1
|
||||
while not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
sub_blocks_added_this_sub_slot += 1
|
||||
|
||||
finished_sub_slots: List[EndOfSubSlotBundle] = [] # Sub-slots since last sub block
|
||||
finished_sub_slots_at_sp: List[EndOfSubSlotBundle] = [] # Sub-slots since last sub block, up to signage point
|
||||
finished_sub_slots_at_ip: List[EndOfSubSlotBundle] = [] # Sub-slots since last sub block, up to infusion point
|
||||
ips: uint64 = latest_sub_block.ips
|
||||
sub_slot_iters: uint64 = calculate_sub_slot_iters(constants, ips) # The number of iterations in one sub-slot
|
||||
same_slot_as_last = True # Only applies to first slot, to prevent old blocks from being added
|
||||
sub_slot_start_total_iters: uint128 = latest_sub_block.total_iters - calculate_ip_iters(
|
||||
constants, latest_sub_block.ips, latest_sub_block.required_iters
|
||||
)
|
||||
sub_slot_start_total_iters: uint128 = latest_sub_block.infusion_sub_slot_total_iters(constants)
|
||||
|
||||
# Start at the last block in block list
|
||||
# Get the challenge for that slot
|
||||
while True:
|
||||
print(
|
||||
"Sub_slot_start_total_iters",
|
||||
)
|
||||
slot_cc_challenge, slot_rc_challenge = get_challenges(
|
||||
sub_blocks, finished_sub_slots, latest_sub_block.header_hash
|
||||
sub_blocks, finished_sub_slots_at_sp, latest_sub_block.header_hash
|
||||
)
|
||||
|
||||
# If did not reach the target slots to skip, don't make any proofs for this sub-slot
|
||||
|
@ -265,72 +262,108 @@ class BlockTools:
|
|||
proofs_of_space = []
|
||||
else:
|
||||
# Get all proofs of space for challenge.
|
||||
proofs_of_space: List[Tuple[uint64, ProofOfSpace]] = self.get_pospaces_for_challenge(
|
||||
proofs_of_space: List[Tuple[str, ProofOfSpace]] = self.get_pospaces_for_challenge(
|
||||
constants,
|
||||
slot_cc_challenge,
|
||||
seed,
|
||||
difficulty,
|
||||
ips,
|
||||
)
|
||||
overflow_pos = []
|
||||
prev_num_of_blocks = num_blocks
|
||||
for required_iters, proof_of_space in sorted(proofs_of_space, key=lambda t: t[0]):
|
||||
if same_slot_as_last and required_iters <= latest_sub_block.required_iters:
|
||||
# Ignore this sub-block because it's in the past
|
||||
continue
|
||||
sp_iters: uint64 = calculate_sp_iters(constants, ips, required_iters)
|
||||
ip_iters = calculate_ip_iters(constants, ips, required_iters)
|
||||
is_overflow_block = sp_iters > ip_iters
|
||||
if force_overflow and not is_overflow_block:
|
||||
continue
|
||||
if is_overflow_block:
|
||||
overflow_pos.append((required_iters, proof_of_space))
|
||||
continue
|
||||
assert latest_sub_block.header_hash in sub_blocks
|
||||
if transaction_data_included:
|
||||
transaction_data = None
|
||||
full_block, sub_block_record = get_full_block_and_sub_record(
|
||||
|
||||
for signage_point_index in range(0, constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA):
|
||||
curr = latest_sub_block
|
||||
while curr.total_iters > sub_slot_start_total_iters + calculate_sp_iters(
|
||||
constants, ips, uint8(signage_point_index)
|
||||
):
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
if curr.total_iters > sub_slot_start_total_iters:
|
||||
finished_sub_slots_at_sp = []
|
||||
|
||||
signage_point: SignagePoint = get_signage_point(
|
||||
constants,
|
||||
sub_slot_start_total_iters,
|
||||
proof_of_space,
|
||||
slot_cc_challenge,
|
||||
slot_rc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
pool_target,
|
||||
start_timestamp,
|
||||
start_height,
|
||||
time_per_sub_block,
|
||||
transaction_data,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
ips,
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
finished_sub_slots,
|
||||
seed,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
latest_sub_block,
|
||||
sub_slot_start_total_iters,
|
||||
uint8(signage_point_index),
|
||||
finished_sub_slots_at_sp,
|
||||
ips,
|
||||
)
|
||||
qualified_proofs: List[Tuple[uint64, ProofOfSpace]] = []
|
||||
for quality_str, proof_of_space in proofs_of_space:
|
||||
if signage_point_index == 0:
|
||||
cc_sp_output_hash: bytes32 = slot_cc_challenge
|
||||
else:
|
||||
assert signage_point is not None
|
||||
cc_sp_output_hash: bytes32 = signage_point.cc_vdf.output.get_hash()
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality_str,
|
||||
proof_of_space.size,
|
||||
difficulty,
|
||||
cc_sp_output_hash,
|
||||
)
|
||||
if required_iters < calculate_sp_interval_iters(constants, ips):
|
||||
qualified_proofs.append((required_iters, proof_of_space))
|
||||
|
||||
if full_block is None:
|
||||
continue
|
||||
if sub_block_record.is_block:
|
||||
transaction_data_included = True
|
||||
for required_iters, proof_of_space in sorted(qualified_proofs, key=lambda t: t[0]):
|
||||
if sub_blocks_added_this_sub_slot == constants.MAX_SLOT_SUB_BLOCKS: # or force_overflow:
|
||||
break
|
||||
if same_slot_as_last:
|
||||
if signage_point_index < latest_sub_block.signage_point_index:
|
||||
# Ignore this sub-block because it's in the past
|
||||
continue
|
||||
if signage_point_index == latest_sub_block.signage_point_index:
|
||||
# Ignore this sub-block because it's in the past
|
||||
if required_iters <= latest_sub_block.required_iters:
|
||||
continue
|
||||
assert latest_sub_block.header_hash in sub_blocks
|
||||
if transaction_data_included:
|
||||
transaction_data = None
|
||||
full_block, sub_block_record = get_full_block_and_sub_record(
|
||||
constants,
|
||||
sub_slot_start_total_iters,
|
||||
uint8(signage_point_index),
|
||||
proof_of_space,
|
||||
slot_cc_challenge,
|
||||
slot_rc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
pool_target,
|
||||
start_timestamp,
|
||||
start_height,
|
||||
time_per_sub_block,
|
||||
transaction_data,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
ips,
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
finished_sub_slots_at_ip,
|
||||
signage_point,
|
||||
seed,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
)
|
||||
|
||||
block_list.append(full_block)
|
||||
num_blocks -= 1
|
||||
if num_blocks == 0:
|
||||
return block_list
|
||||
print(f"Block tools: created block {full_block.height} {full_block.total_iters}, not overflow")
|
||||
sub_blocks[full_block.header_hash] = sub_block_record
|
||||
height_to_hash[uint32(full_block.height)] = full_block.header_hash
|
||||
latest_sub_block = sub_blocks[full_block.header_hash]
|
||||
finished_sub_slots = []
|
||||
if full_block is None:
|
||||
continue
|
||||
if sub_block_record.is_block:
|
||||
transaction_data_included = True
|
||||
|
||||
block_list.append(full_block)
|
||||
sub_blocks_added_this_sub_slot += 1
|
||||
num_blocks -= 1
|
||||
if num_blocks == 0:
|
||||
return block_list
|
||||
sub_blocks[full_block.header_hash] = sub_block_record
|
||||
print(f"Added block {sub_block_record.height} overflow=False, iters {sub_block_record.total_iters}")
|
||||
height_to_hash[uint32(full_block.height)] = full_block.header_hash
|
||||
latest_sub_block = sub_blocks[full_block.header_hash]
|
||||
finished_sub_slots_at_ip = []
|
||||
|
||||
# Finish the end of sub-slot and try again next sub-slot
|
||||
# End of sub-slot logic
|
||||
if len(finished_sub_slots) == 0:
|
||||
if len(finished_sub_slots_at_ip) == 0:
|
||||
# Sub block has been created within this sub-slot
|
||||
eos_iters = sub_slot_iters - (latest_sub_block.total_iters - sub_slot_start_total_iters)
|
||||
cc_input = latest_sub_block.challenge_vdf_output
|
||||
|
@ -362,7 +395,7 @@ class BlockTools:
|
|||
icc_ip_vdf, icc_ip_proof = get_icc(
|
||||
constants,
|
||||
sub_slot_start_total_iters + sub_slot_iters,
|
||||
finished_sub_slots,
|
||||
finished_sub_slots_at_ip,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
sub_slot_start_total_iters,
|
||||
|
@ -374,28 +407,32 @@ class BlockTools:
|
|||
cc_vdf.challenge_hash, ClassgroupElement.get_default_element(), sub_slot_iters, cc_vdf.output
|
||||
)
|
||||
|
||||
sub_epoch_summary: Optional[SubEpochSummary] = handle_end_of_sub_epoch(
|
||||
sub_epoch_summary: Optional[SubEpochSummary] = next_sub_epoch_summary(
|
||||
constants,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
latest_sub_block.signage_point_index,
|
||||
latest_sub_block.required_iters,
|
||||
block_list[-1],
|
||||
)
|
||||
|
||||
if sub_epoch_summary is not None:
|
||||
ses_hash = sub_epoch_summary.get_hash()
|
||||
new_ips: Optional[uint64] = sub_epoch_summary.new_ips
|
||||
new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty
|
||||
print(f"Block tools: sub epoch summary: {sub_epoch_summary}")
|
||||
if new_ips is not None:
|
||||
ips = new_ips
|
||||
difficulty = new_difficulty
|
||||
overflow_pos = [] # No overflow blocks on new difficulty
|
||||
print("Sub epoch summary:", sub_epoch_summary)
|
||||
else:
|
||||
ses_hash = None
|
||||
new_ips = None
|
||||
new_difficulty = None
|
||||
|
||||
if icc_ip_vdf is not None:
|
||||
if len(finished_sub_slots) == 0:
|
||||
# Icc vdf (Deficit of latest sub-block is <= 4)
|
||||
if len(finished_sub_slots_at_ip) == 0:
|
||||
# This means there are sub-blocks in this sub-slot
|
||||
curr = latest_sub_block
|
||||
while not curr.is_challenge_sub_block(constants) and not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
@ -404,6 +441,7 @@ class BlockTools:
|
|||
else:
|
||||
icc_eos_iters = sub_slot_iters
|
||||
else:
|
||||
# This means there are no sub-blocks in this sub-slot
|
||||
icc_eos_iters = sub_slot_iters
|
||||
icc_ip_vdf = VDFInfo(
|
||||
icc_ip_vdf.challenge_hash,
|
||||
|
@ -415,10 +453,11 @@ class BlockTools:
|
|||
icc_sub_slot_hash = icc_sub_slot.get_hash() if latest_sub_block.deficit == 0 else None
|
||||
cc_sub_slot = ChallengeChainSubSlot(cc_vdf, icc_sub_slot_hash, ses_hash, new_ips, new_difficulty)
|
||||
else:
|
||||
# No icc
|
||||
icc_sub_slot = None
|
||||
cc_sub_slot = ChallengeChainSubSlot(cc_vdf, None, ses_hash, new_ips, new_difficulty)
|
||||
|
||||
finished_sub_slots.append(
|
||||
finished_sub_slots_at_ip.append(
|
||||
EndOfSubSlotBundle(
|
||||
cc_sub_slot,
|
||||
icc_sub_slot,
|
||||
|
@ -431,54 +470,103 @@ class BlockTools:
|
|||
SubSlotProofs(cc_proof, icc_ip_proof, rc_proof),
|
||||
)
|
||||
)
|
||||
overflow_cc_challenge = finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
overflow_rc_challenge = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
finished_sub_slots_eos = finished_sub_slots_at_ip.copy()
|
||||
overflow_cc_challenge = finished_sub_slots_at_ip[-1].challenge_chain.get_hash()
|
||||
overflow_rc_challenge = finished_sub_slots_at_ip[-1].reward_chain.get_hash()
|
||||
|
||||
if transaction_data_included:
|
||||
transaction_data = None
|
||||
for required_iters, proof_of_space in overflow_pos:
|
||||
full_block, sub_block_record = get_full_block_and_sub_record(
|
||||
constants,
|
||||
sub_slot_start_total_iters,
|
||||
proof_of_space,
|
||||
slot_cc_challenge,
|
||||
slot_rc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
pool_target,
|
||||
start_timestamp,
|
||||
start_height,
|
||||
time_per_sub_block,
|
||||
transaction_data,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
ips,
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
finished_sub_slots,
|
||||
seed,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
overflow_cc_challenge=overflow_cc_challenge,
|
||||
overflow_rc_challenge=overflow_rc_challenge,
|
||||
)
|
||||
sub_blocks_added_this_sub_slot = 0 # Sub slot ended, overflows are in next sub slot
|
||||
|
||||
if full_block is None:
|
||||
continue
|
||||
if sub_block_record.is_block:
|
||||
transaction_data_included = True
|
||||
if new_ips is None:
|
||||
# No overflows on new epoch
|
||||
for signage_point_index in range(
|
||||
constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA, constants.NUM_SPS_SUB_SLOT
|
||||
):
|
||||
curr = latest_sub_block
|
||||
while curr.total_iters > sub_slot_start_total_iters + calculate_sp_iters(
|
||||
constants, ips, uint8(signage_point_index)
|
||||
):
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
if curr.total_iters > sub_slot_start_total_iters:
|
||||
finished_sub_slots_at_sp = []
|
||||
signage_point: SignagePoint = get_signage_point(
|
||||
constants,
|
||||
sub_blocks,
|
||||
latest_sub_block,
|
||||
sub_slot_start_total_iters,
|
||||
uint8(signage_point_index),
|
||||
finished_sub_slots_at_sp,
|
||||
ips,
|
||||
)
|
||||
qualified_proofs: List[Tuple[uint64, ProofOfSpace]] = []
|
||||
for quality_str, proof_of_space in proofs_of_space:
|
||||
if signage_point_index == 0:
|
||||
cc_sp_output_hash: bytes32 = slot_cc_challenge
|
||||
else:
|
||||
assert signage_point is not None
|
||||
cc_sp_output_hash: bytes32 = signage_point.cc_vdf.output.get_hash()
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality_str,
|
||||
proof_of_space.size,
|
||||
difficulty,
|
||||
cc_sp_output_hash,
|
||||
)
|
||||
if required_iters < calculate_sp_interval_iters(constants, ips):
|
||||
qualified_proofs.append((required_iters, proof_of_space))
|
||||
for required_iters, proof_of_space in sorted(qualified_proofs, key=lambda t: t[0]):
|
||||
if sub_blocks_added_this_sub_slot == constants.MAX_SLOT_SUB_BLOCKS:
|
||||
break
|
||||
full_block, sub_block_record = get_full_block_and_sub_record(
|
||||
constants,
|
||||
sub_slot_start_total_iters,
|
||||
uint8(signage_point_index),
|
||||
proof_of_space,
|
||||
slot_cc_challenge,
|
||||
slot_rc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
pool_target,
|
||||
start_timestamp,
|
||||
start_height,
|
||||
time_per_sub_block,
|
||||
transaction_data,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
ips,
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
finished_sub_slots_at_ip,
|
||||
signage_point,
|
||||
seed,
|
||||
latest_sub_block,
|
||||
sub_blocks,
|
||||
overflow_cc_challenge=overflow_cc_challenge,
|
||||
overflow_rc_challenge=overflow_rc_challenge,
|
||||
)
|
||||
|
||||
block_list.append(full_block)
|
||||
num_blocks -= 1
|
||||
if num_blocks == 0:
|
||||
return block_list
|
||||
print(f"Block tools: created block {full_block.height} {full_block.total_iters}, overflow")
|
||||
if full_block is None:
|
||||
continue
|
||||
if sub_block_record.is_block:
|
||||
transaction_data_included = True
|
||||
|
||||
sub_blocks[full_block.header_hash] = sub_block_record
|
||||
height_to_hash[uint32(full_block.height)] = full_block.header_hash
|
||||
latest_sub_block = sub_blocks[full_block.header_hash]
|
||||
finished_sub_slots = []
|
||||
block_list.append(full_block)
|
||||
sub_blocks_added_this_sub_slot += 1
|
||||
print(
|
||||
f"Added block {sub_block_record.height } overflow=True, iters {sub_block_record.total_iters}"
|
||||
)
|
||||
num_blocks -= 1
|
||||
if num_blocks == 0:
|
||||
return block_list
|
||||
|
||||
sub_blocks[full_block.header_hash] = sub_block_record
|
||||
height_to_hash[uint32(full_block.height)] = full_block.header_hash
|
||||
latest_sub_block = sub_blocks[full_block.header_hash]
|
||||
finished_sub_slots_at_ip = []
|
||||
|
||||
finished_sub_slots_at_sp = finished_sub_slots_eos.copy()
|
||||
same_slot_as_last = False
|
||||
sub_slot_start_total_iters += sub_slot_iters
|
||||
sub_slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
|
@ -508,68 +596,95 @@ class BlockTools:
|
|||
# Keep trying until we get a good proof of space that also passes sp filter
|
||||
while True:
|
||||
cc_challenge, rc_challenge = get_genesis_challenges(constants, finished_sub_slots)
|
||||
proofs_of_space: List[Tuple[uint64, ProofOfSpace]] = self.get_pospaces_for_challenge(
|
||||
proofs_of_space: List[Tuple[str, ProofOfSpace]] = self.get_pospaces_for_challenge(
|
||||
constants,
|
||||
cc_challenge,
|
||||
seed,
|
||||
uint64(constants.DIFFICULTY_STARTING),
|
||||
uint64(constants.IPS_STARTING),
|
||||
)
|
||||
|
||||
# Try each of the proofs of space
|
||||
for required_iters, proof_of_space in sorted(proofs_of_space, key=lambda t: t[0]):
|
||||
sp_iters: uint64 = calculate_sp_iters(constants, uint64(constants.IPS_STARTING), required_iters)
|
||||
ip_iters = calculate_ip_iters(constants, uint64(constants.IPS_STARTING), required_iters)
|
||||
is_overflow_block = sp_iters > ip_iters
|
||||
if force_overflow and not is_overflow_block:
|
||||
continue
|
||||
if len(finished_sub_slots) < skip_slots:
|
||||
continue
|
||||
|
||||
unfinished_block = create_unfinished_block(
|
||||
for signage_point_index in range(0, constants.NUM_SPS_SUB_SLOT):
|
||||
signage_point: SignagePoint = get_signage_point(
|
||||
constants,
|
||||
{},
|
||||
None,
|
||||
sub_slot_total_iters,
|
||||
sp_iters,
|
||||
ip_iters,
|
||||
proof_of_space,
|
||||
cc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
PoolTarget(constants.GENESIS_PRE_FARM_POOL_PUZZLE_HASH, uint32(0)),
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
timestamp,
|
||||
seed,
|
||||
finished_sub_slots=finished_sub_slots,
|
||||
uint8(signage_point_index),
|
||||
finished_sub_slots,
|
||||
constants.IPS_STARTING,
|
||||
)
|
||||
if unfinished_block is None:
|
||||
continue
|
||||
|
||||
if not is_overflow_block:
|
||||
cc_ip_vdf, cc_ip_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
cc_challenge,
|
||||
ip_iters,
|
||||
qualified_proofs: List[Tuple[uint64, ProofOfSpace]] = []
|
||||
for quality_str, proof_of_space in proofs_of_space:
|
||||
if signage_point_index == 0:
|
||||
cc_sp_output_hash: bytes32 = cc_challenge
|
||||
else:
|
||||
assert signage_point is not None
|
||||
cc_sp_output_hash: bytes32 = signage_point.cc_vdf.output.get_hash()
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality_str,
|
||||
proof_of_space.size,
|
||||
constants.DIFFICULTY_STARTING,
|
||||
cc_sp_output_hash,
|
||||
)
|
||||
rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof(
|
||||
if required_iters < calculate_sp_interval_iters(constants, constants.IPS_STARTING):
|
||||
qualified_proofs.append((required_iters, proof_of_space))
|
||||
# Try each of the proofs of space
|
||||
for required_iters, proof_of_space in qualified_proofs:
|
||||
sp_iters: uint64 = calculate_sp_iters(
|
||||
constants, uint64(constants.IPS_STARTING), uint8(signage_point_index)
|
||||
)
|
||||
ip_iters = calculate_ip_iters(
|
||||
constants, uint64(constants.IPS_STARTING), uint8(signage_point_index), required_iters
|
||||
)
|
||||
is_overflow_block = is_overflow_sub_block(constants, uint8(signage_point_index))
|
||||
if force_overflow and not is_overflow_block:
|
||||
continue
|
||||
if len(finished_sub_slots) < skip_slots:
|
||||
continue
|
||||
|
||||
unfinished_block = create_unfinished_block(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
rc_challenge,
|
||||
sub_slot_total_iters,
|
||||
uint8(signage_point_index),
|
||||
sp_iters,
|
||||
ip_iters,
|
||||
proof_of_space,
|
||||
cc_challenge,
|
||||
farmer_reward_puzzle_hash,
|
||||
PoolTarget(constants.GENESIS_PRE_FARM_POOL_PUZZLE_HASH, uint32(0)),
|
||||
self.get_plot_signature,
|
||||
self.get_pool_key_signature,
|
||||
signage_point,
|
||||
timestamp,
|
||||
seed,
|
||||
finished_sub_slots=finished_sub_slots,
|
||||
)
|
||||
assert unfinished_block is not None
|
||||
return unfinished_block_to_full_block(
|
||||
unfinished_block,
|
||||
cc_ip_vdf,
|
||||
cc_ip_proof,
|
||||
rc_ip_vdf,
|
||||
rc_ip_proof,
|
||||
None,
|
||||
None,
|
||||
finished_sub_slots,
|
||||
None,
|
||||
constants.DIFFICULTY_STARTING,
|
||||
)
|
||||
if not is_overflow_block:
|
||||
cc_ip_vdf, cc_ip_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
cc_challenge,
|
||||
ip_iters,
|
||||
)
|
||||
rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
rc_challenge,
|
||||
ip_iters,
|
||||
)
|
||||
assert unfinished_block is not None
|
||||
return unfinished_block_to_full_block(
|
||||
unfinished_block,
|
||||
cc_ip_vdf,
|
||||
cc_ip_proof,
|
||||
rc_ip_vdf,
|
||||
rc_ip_proof,
|
||||
None,
|
||||
None,
|
||||
finished_sub_slots,
|
||||
None,
|
||||
constants.DIFFICULTY_STARTING,
|
||||
)
|
||||
|
||||
# Finish the end of sub-slot and try again next sub-slot
|
||||
cc_vdf, cc_proof = get_vdf_info_and_proof(
|
||||
|
@ -626,8 +741,8 @@ class BlockTools:
|
|||
sub_slot_total_iters += sub_slot_iters
|
||||
|
||||
def get_pospaces_for_challenge(
|
||||
self, constants: ConsensusConstants, challenge_hash: bytes32, seed: bytes, difficulty: uint64, ips: uint64
|
||||
) -> (ProofOfSpace, uint64):
|
||||
self, constants: ConsensusConstants, challenge_hash: bytes32, seed: bytes
|
||||
) -> List[Tuple[str, ProofOfSpace]]:
|
||||
found_proofs: List[(uint64, ProofOfSpace)] = []
|
||||
plots: List[PlotInfo] = [
|
||||
plot_info for _, plot_info in sorted(list(self.plots.items()), key=lambda x: str(x[0]))
|
||||
|
@ -638,32 +753,24 @@ class BlockTools:
|
|||
for plot_info in plots:
|
||||
# Allow passing in seed, to create reorgs and different chains
|
||||
plot_id = plot_info.prover.get_id()
|
||||
if ProofOfSpace.can_create_proof(constants, plot_id, challenge_hash, None, None):
|
||||
if ProofOfSpace.passes_plot_filter(constants, plot_id, challenge_hash):
|
||||
passed_plot_filter += 1
|
||||
qualities = plot_info.prover.get_qualities_for_challenge(challenge_hash)
|
||||
for proof_index, quality_str in enumerate(qualities):
|
||||
sub_slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
quality_str,
|
||||
plot_info.prover.get_size(),
|
||||
difficulty,
|
||||
proof_xs: bytes = plot_info.prover.get_full_proof(challenge_hash, proof_index)
|
||||
plot_pk = ProofOfSpace.generate_plot_public_key(
|
||||
plot_info.local_sk.get_g1(),
|
||||
plot_info.farmer_public_key,
|
||||
)
|
||||
if required_iters < sub_slot_iters:
|
||||
proof_xs: bytes = plot_info.prover.get_full_proof(challenge_hash, proof_index)
|
||||
|
||||
plot_pk = ProofOfSpace.generate_plot_public_key(
|
||||
plot_info.local_sk.get_g1(),
|
||||
plot_info.farmer_public_key,
|
||||
)
|
||||
proof_of_space: ProofOfSpace = ProofOfSpace(
|
||||
challenge_hash,
|
||||
plot_info.pool_public_key,
|
||||
None,
|
||||
plot_pk,
|
||||
plot_info.prover.get_size(),
|
||||
proof_xs,
|
||||
)
|
||||
found_proofs.append((required_iters, proof_of_space))
|
||||
proof_of_space: ProofOfSpace = ProofOfSpace(
|
||||
challenge_hash,
|
||||
plot_info.pool_public_key,
|
||||
None,
|
||||
plot_pk,
|
||||
plot_info.prover.get_size(),
|
||||
proof_xs,
|
||||
)
|
||||
found_proofs.append((quality_str, proof_of_space))
|
||||
if len(found_proofs) >= 4:
|
||||
# Removes some proofs of space to create "random" chains, based on the seed
|
||||
random_sample = random.sample(found_proofs, len(found_proofs) - 3)
|
||||
|
@ -676,12 +783,108 @@ class BlockTools:
|
|||
return random_sample
|
||||
|
||||
|
||||
def get_signage_point(
|
||||
constants: ConsensusConstants,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
latest_sub_block: Optional[SubBlockRecord],
|
||||
sub_slot_start_total_iters: uint128,
|
||||
signage_point_index: uint8,
|
||||
finished_sub_slots: List[EndOfSubSlotBundle],
|
||||
ips: uint64,
|
||||
) -> SignagePoint:
|
||||
if signage_point_index == 0:
|
||||
return SignagePoint(None, None, None, None)
|
||||
|
||||
is_genesis = latest_sub_block is None
|
||||
sp_iters = calculate_sp_iters(constants, ips, signage_point_index)
|
||||
overflow = is_overflow_sub_block(constants, signage_point_index)
|
||||
total_iters_sp: uint128 = sub_slot_start_total_iters + calculate_sp_iters(constants, ips, signage_point_index)
|
||||
|
||||
new_sub_slot = len(finished_sub_slots) > 0
|
||||
|
||||
if is_genesis:
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
if len(finished_sub_slots) == 0:
|
||||
cc_vdf_challenge = constants.FIRST_CC_CHALLENGE
|
||||
rc_vdf_challenge = constants.FIRST_RC_CHALLENGE
|
||||
else:
|
||||
cc_vdf_challenge = finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
rc_vdf_challenge = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
elif new_sub_slot and not overflow:
|
||||
# Start from start of this slot. Case of no overflow slots. Also includes genesis block after
|
||||
# empty slot (but not overflowing)
|
||||
rc_vdf_challenge: bytes32 = finished_sub_slots[-1].reward_chain.get_hash()
|
||||
cc_vdf_challenge = finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
elif new_sub_slot and overflow and len(finished_sub_slots) > 1:
|
||||
# Start from start of prev slot. Rare case of empty prev slot.
|
||||
# Includes genesis block after 2 empty slots
|
||||
rc_vdf_challenge = finished_sub_slots[-2].reward_chain.get_hash()
|
||||
cc_vdf_challenge = finished_sub_slots[-2].challenge_chain.get_hash()
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
else:
|
||||
if new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev will skip 1 sub-slot
|
||||
elif not new_sub_slot and overflow:
|
||||
num_sub_slots_to_look_for = 2 # Starting at prev does not skip any sub slots
|
||||
elif not new_sub_slot and not overflow:
|
||||
num_sub_slots_to_look_for = 1 # Starting at prev does not skip any sub slots, but we should not go back
|
||||
else:
|
||||
assert False
|
||||
|
||||
next_sb: SubBlockRecord = latest_sub_block
|
||||
curr: SubBlockRecord = next_sb
|
||||
|
||||
# Finds a sub-block which is BEFORE our signage point, otherwise goes back to the end of sub-slot
|
||||
# Note that for overflow sub-blocks, we are looking at the end of the previous sub-slot
|
||||
while num_sub_slots_to_look_for > 0:
|
||||
next_sb = curr
|
||||
if curr.first_in_sub_slot:
|
||||
num_sub_slots_to_look_for -= 1
|
||||
if curr.total_iters < total_iters_sp:
|
||||
break
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
if curr.total_iters < total_iters_sp:
|
||||
sp_vdf_iters = total_iters_sp - curr.total_iters
|
||||
cc_vdf_input = curr.challenge_vdf_output
|
||||
rc_vdf_challenge = curr.reward_infusion_new_challenge
|
||||
else:
|
||||
sp_vdf_iters = sp_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
rc_vdf_challenge = next_sb.finished_reward_slot_hashes[-1]
|
||||
|
||||
while not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
cc_vdf_challenge = curr.finished_challenge_slot_hashes[-1]
|
||||
|
||||
cc_sp_vdf, cc_sp_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
cc_vdf_input,
|
||||
cc_vdf_challenge,
|
||||
sp_vdf_iters,
|
||||
)
|
||||
rc_sp_vdf, rc_sp_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
ClassgroupElement.get_default_element(),
|
||||
rc_vdf_challenge,
|
||||
sp_vdf_iters,
|
||||
)
|
||||
return SignagePoint(cc_sp_vdf, cc_sp_proof, rc_sp_vdf, rc_sp_proof)
|
||||
|
||||
|
||||
def finish_sub_block(
|
||||
constants: ConsensusConstants,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
finished_sub_slots: List[EndOfSubSlotBundle],
|
||||
sub_slot_start_total_iters: uint128,
|
||||
signage_point_index: uint8,
|
||||
unfinished_block: UnfinishedBlock,
|
||||
required_iters: uint64,
|
||||
ip_iters: uint64,
|
||||
|
@ -691,7 +894,7 @@ def finish_sub_block(
|
|||
ips: uint64,
|
||||
difficulty: uint64,
|
||||
):
|
||||
is_overflow = required_iters > ip_iters
|
||||
is_overflow = is_overflow_sub_block(constants, signage_point_index)
|
||||
cc_vdf_challenge = slot_cc_challenge
|
||||
slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
if len(finished_sub_slots) == 0:
|
||||
|
@ -786,7 +989,9 @@ def get_plot_dir():
|
|||
return cache_path
|
||||
|
||||
|
||||
def load_block_list(block_list, constants) -> (Dict[uint32, bytes32], uint64, Dict[uint32, SubBlockRecord]):
|
||||
def load_block_list(
|
||||
block_list: List[FullBlock], constants
|
||||
) -> (Dict[uint32, bytes32], uint64, Dict[uint32, SubBlockRecord]):
|
||||
difficulty = 0
|
||||
height_to_hash: Dict[uint32, bytes32] = {}
|
||||
sub_blocks: Dict[uint32, SubBlockRecord] = {}
|
||||
|
@ -796,11 +1001,26 @@ def load_block_list(block_list, constants) -> (Dict[uint32, bytes32], uint64, Di
|
|||
else:
|
||||
difficulty = full_block.weight - block_list[full_block.height - 1].weight
|
||||
quality_str = full_block.reward_chain_sub_block.proof_of_space.verify_and_get_quality_string(constants)
|
||||
if full_block.reward_chain_sub_block.signage_point_index == 0:
|
||||
if len(full_block.finished_sub_slots) > 0:
|
||||
sp_hash = full_block.finished_sub_slots[-1].challenge_chain.get_hash()
|
||||
else:
|
||||
if full_block.height == 0:
|
||||
sp_hash = constants.FIRST_CC_CHALLENGE
|
||||
else:
|
||||
curr = sub_blocks[full_block.prev_header_hash]
|
||||
while not curr.first_in_sub_slot:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
sp_hash = curr.finished_challenge_slot_hashes[-1]
|
||||
else:
|
||||
sp_hash = full_block.reward_chain_sub_block.challenge_chain_sp_vdf.output.get_hash()
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
quality_str,
|
||||
full_block.reward_chain_sub_block.proof_of_space.size,
|
||||
difficulty,
|
||||
sp_hash,
|
||||
)
|
||||
|
||||
sub_blocks[full_block.header_hash] = full_block_to_sub_block_record(
|
||||
constants,
|
||||
sub_blocks,
|
||||
|
@ -856,8 +1076,6 @@ def get_icc(
|
|||
else:
|
||||
# First sub block in sub slot has deficit 0,1,2 or 3
|
||||
icc_challenge_hash = curr.finished_infused_challenge_slot_hashes[-1]
|
||||
if icc_input is None:
|
||||
print(deficit, icc_input, latest_sub_block, finished_sub_slots)
|
||||
return get_vdf_info_and_proof(
|
||||
constants,
|
||||
icc_input,
|
||||
|
@ -866,60 +1084,10 @@ def get_icc(
|
|||
)
|
||||
|
||||
|
||||
def handle_end_of_sub_epoch(
|
||||
constants: ConsensusConstants,
|
||||
last_block: SubBlockRecord,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
) -> Optional[SubEpochSummary]:
|
||||
fs = finishes_sub_epoch(constants, last_block.height, last_block.deficit, False, sub_blocks, last_block.prev_hash)
|
||||
fe = finishes_sub_epoch(constants, last_block.height, last_block.deficit, True, sub_blocks, last_block.prev_hash)
|
||||
|
||||
if not fs: # Does not finish sub-epoch
|
||||
return None
|
||||
|
||||
if not fe:
|
||||
# Does not finish epoch
|
||||
new_difficulty: Optional[uint64] = None
|
||||
new_ips: Optional[uint64] = None
|
||||
else:
|
||||
ip_iters = calculate_ip_iters(constants, last_block.ips, last_block.required_iters)
|
||||
sp_iters = calculate_sp_iters(constants, last_block.ips, last_block.required_iters)
|
||||
new_difficulty = get_next_difficulty(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
last_block.header_hash,
|
||||
last_block.height,
|
||||
uint64(last_block.weight - sub_blocks[last_block.prev_hash].weight),
|
||||
last_block.deficit,
|
||||
True,
|
||||
uint128(last_block.total_iters - ip_iters + sp_iters),
|
||||
)
|
||||
new_ips = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
last_block.header_hash,
|
||||
last_block.height,
|
||||
last_block.ips,
|
||||
last_block.deficit,
|
||||
True,
|
||||
uint128(last_block.total_iters - ip_iters + sp_iters),
|
||||
)
|
||||
return make_sub_epoch_summary(
|
||||
constants,
|
||||
sub_blocks,
|
||||
last_block.height + 1,
|
||||
last_block,
|
||||
new_difficulty,
|
||||
new_ips,
|
||||
)
|
||||
|
||||
|
||||
def get_full_block_and_sub_record(
|
||||
constants: ConsensusConstants,
|
||||
sub_slot_start_total_iters: uint128,
|
||||
signage_point_index: uint8,
|
||||
proof_of_space: ProofOfSpace,
|
||||
slot_cc_challenge: bytes32,
|
||||
slot_rc_challenge: bytes32,
|
||||
|
@ -936,18 +1104,20 @@ def get_full_block_and_sub_record(
|
|||
get_plot_signature: Callable[[bytes32, G1Element], G2Element],
|
||||
get_pool_signature: Callable[[PoolTarget, G1Element], G2Element],
|
||||
finished_sub_slots: List[EndOfSubSlotBundle],
|
||||
signage_point: SignagePoint,
|
||||
seed: bytes = b"",
|
||||
prev_sub_block: Optional[SubBlockRecord] = None,
|
||||
sub_blocks: Dict[uint32, SubBlockRecord] = None,
|
||||
overflow_cc_challenge: bytes32 = None,
|
||||
overflow_rc_challenge: bytes32 = None,
|
||||
) -> Tuple[FullBlock, SubBlockRecord]:
|
||||
sp_iters = calculate_sp_iters(constants, ips, required_iters)
|
||||
ip_iters = calculate_ip_iters(constants, ips, required_iters)
|
||||
sp_iters = calculate_sp_iters(constants, ips, signage_point_index)
|
||||
ip_iters = calculate_ip_iters(constants, ips, signage_point_index, required_iters)
|
||||
|
||||
unfinished_block = create_unfinished_block(
|
||||
constants,
|
||||
sub_slot_start_total_iters,
|
||||
signage_point_index,
|
||||
sp_iters,
|
||||
ip_iters,
|
||||
proof_of_space,
|
||||
|
@ -956,6 +1126,7 @@ def get_full_block_and_sub_record(
|
|||
pool_target,
|
||||
get_plot_signature,
|
||||
get_pool_signature,
|
||||
signage_point,
|
||||
uint64(start_timestamp + int((prev_sub_block.height + 1 - start_height) * time_per_sub_block)),
|
||||
seed,
|
||||
transaction_data,
|
||||
|
@ -963,8 +1134,6 @@ def get_full_block_and_sub_record(
|
|||
sub_blocks,
|
||||
finished_sub_slots,
|
||||
)
|
||||
if unfinished_block is None:
|
||||
return None, None
|
||||
|
||||
if (overflow_cc_challenge is not None) and (overflow_rc_challenge is not None):
|
||||
slot_cc_challenge = overflow_cc_challenge
|
||||
|
@ -976,6 +1145,7 @@ def get_full_block_and_sub_record(
|
|||
height_to_hash,
|
||||
finished_sub_slots,
|
||||
sub_slot_start_total_iters,
|
||||
signage_point_index,
|
||||
unfinished_block,
|
||||
required_iters,
|
||||
ip_iters,
|
||||
|
|
|
@ -124,6 +124,7 @@ class Err(Enum):
|
|||
INVALID_ICC_HASH_CC = 98
|
||||
INVALID_ICC_HASH_RC = 99
|
||||
INVALID_ICC_EOS_VDF = 100
|
||||
INVALID_SP_INDEX = 101
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
|
|
|
@ -8,7 +8,7 @@ def truncate_to_significant_bits(input_x: int, num_significant_bits: int) -> int
|
|||
if num_significant_bits > x.bit_length():
|
||||
return x
|
||||
lower = x.bit_length() - num_significant_bits
|
||||
mask = (1 << (x.bit_length())) - 1 - ((1 << (lower)) - 1)
|
||||
mask = (1 << (x.bit_length())) - 1 - ((1 << lower) - 1)
|
||||
if input_x < 0:
|
||||
return -(x & mask)
|
||||
else:
|
||||
|
|
|
@ -13,8 +13,7 @@ from pytest import raises
|
|||
|
||||
test_constants = DEFAULT_CONSTANTS.replace(
|
||||
**{
|
||||
"EXTRA_ITERS_TIME_TARGET": 37.5,
|
||||
"NUM_CHECKPOINTS_PER_SLOT": 32,
|
||||
"NUM_SPS_SUB_SLOT": 32,
|
||||
"SLOT_TIME_TARGET": 300,
|
||||
}
|
||||
)
|
||||
|
@ -26,73 +25,63 @@ class TestPotIterations:
|
|||
assert calculate_sub_slot_iters(test_constants, ips) == test_constants.SLOT_TIME_TARGET * ips
|
||||
|
||||
def test_is_overflow_sub_block(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
assert not is_overflow_sub_block(test_constants, uint8(27))
|
||||
assert not is_overflow_sub_block(test_constants, uint8(28))
|
||||
assert is_overflow_sub_block(test_constants, uint8(29))
|
||||
assert is_overflow_sub_block(test_constants, uint8(30))
|
||||
assert is_overflow_sub_block(test_constants, uint8(31))
|
||||
with raises(ValueError):
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1))
|
||||
|
||||
assert is_overflow_sub_block(
|
||||
constants,
|
||||
ips,
|
||||
uint64(test_constants.SLOT_TIME_TARGET * ips - int(test_constants.EXTRA_ITERS_TIME_TARGET * ips)),
|
||||
)
|
||||
assert not is_overflow_sub_block(
|
||||
constants,
|
||||
ips,
|
||||
uint64(test_constants.SLOT_TIME_TARGET * ips - int(test_constants.EXTRA_ITERS_TIME_TARGET * ips) - 1),
|
||||
)
|
||||
assert not is_overflow_sub_block(constants, ips, uint64(0))
|
||||
assert is_overflow_sub_block(test_constants, uint8(32))
|
||||
|
||||
def test_calculate_sp_iters(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
with raises(ValueError):
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
one_checkpoint_iters = test_constants.SLOT_TIME_TARGET * ips // 32
|
||||
assert (
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1))
|
||||
== 31 * one_checkpoint_iters
|
||||
)
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters)) == 20 * one_checkpoint_iters
|
||||
assert (
|
||||
calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters) - 1) == 19 * one_checkpoint_iters
|
||||
)
|
||||
assert (
|
||||
calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters) + 1) == 20 * one_checkpoint_iters
|
||||
)
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(1)) == 0 * one_checkpoint_iters
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(0)) == 0 * one_checkpoint_iters
|
||||
calculate_sp_iters(test_constants, ips, uint8(32))
|
||||
calculate_sp_iters(test_constants, ips, uint8(31))
|
||||
|
||||
def test_calculate_ip_iters(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
extra_iters = int(test_constants.EXTRA_ITERS_TIME_TARGET * int(ips))
|
||||
one_checkpoint_iters = test_constants.SLOT_TIME_TARGET * ips // test_constants.NUM_CHECKPOINTS_PER_SLOT
|
||||
with raises(ValueError):
|
||||
calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
sp_interval_iters = test_constants.SLOT_TIME_TARGET * ips // test_constants.NUM_SPS_SUB_SLOT
|
||||
|
||||
assert calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1)) == (
|
||||
(test_constants.SLOT_TIME_TARGET * ips - 1) + extra_iters
|
||||
) - (test_constants.SLOT_TIME_TARGET * ips)
|
||||
assert (
|
||||
calculate_ip_iters(constants, ips, uint64(5 * one_checkpoint_iters))
|
||||
== 5 * one_checkpoint_iters + extra_iters
|
||||
)
|
||||
assert (
|
||||
calculate_ip_iters(test_constants, ips, uint64(5 * one_checkpoint_iters + 678))
|
||||
== 5 * one_checkpoint_iters + extra_iters + 678
|
||||
)
|
||||
assert (
|
||||
calculate_ip_iters(test_constants, ips, uint64(5 * one_checkpoint_iters - 567))
|
||||
== 5 * one_checkpoint_iters + extra_iters - 567
|
||||
)
|
||||
assert calculate_ip_iters(constants, ips, uint64(0)) == extra_iters
|
||||
assert calculate_ip_iters(constants, ips, uint64(1)) == extra_iters + 1
|
||||
with raises(ValueError):
|
||||
# Invalid signage point index
|
||||
calculate_ip_iters(test_constants, ips, uint8(123), uint64(100000))
|
||||
|
||||
sp_iters = sp_interval_iters * 13
|
||||
|
||||
with raises(ValueError):
|
||||
# required_iters too high
|
||||
calculate_ip_iters(test_constants, ips, sp_interval_iters, sp_interval_iters)
|
||||
|
||||
with raises(ValueError):
|
||||
# required_iters too high
|
||||
calculate_ip_iters(test_constants, ips, sp_interval_iters, sp_interval_iters * 12)
|
||||
|
||||
with raises(ValueError):
|
||||
# required_iters too low (0)
|
||||
calculate_ip_iters(test_constants, ips, sp_interval_iters, uint64(0))
|
||||
|
||||
required_iters = sp_interval_iters - 1
|
||||
ip_iters = calculate_ip_iters(test_constants, ips, 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)
|
||||
ip_iters = calculate_ip_iters(test_constants, ips, uint8(13), required_iters)
|
||||
assert ip_iters == sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters
|
||||
|
||||
required_iters = ips * 4
|
||||
ip_iters = calculate_ip_iters(test_constants, ips, uint8(13), required_iters)
|
||||
assert ip_iters == sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters
|
||||
assert sp_iters < ip_iters
|
||||
|
||||
# Overflow
|
||||
sp_iters = sp_interval_iters * (test_constants.NUM_SPS_SUB_SLOT - 1)
|
||||
ip_iters = calculate_ip_iters(test_constants, ips, uint8(test_constants.NUM_SPS_SUB_SLOT - 1), required_iters)
|
||||
assert ip_iters == (
|
||||
sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters
|
||||
) % calculate_sub_slot_iters(test_constants, ips)
|
||||
assert sp_iters > ip_iters
|
||||
|
||||
def test_win_percentage(self):
|
||||
"""
|
||||
|
@ -100,8 +89,8 @@ class TestPotIterations:
|
|||
with the assumption that all farmers have access to the same VDF speed.
|
||||
"""
|
||||
farmer_ks = {
|
||||
uint8(32): 300,
|
||||
uint8(33): 100,
|
||||
uint8(32): 200,
|
||||
uint8(33): 200,
|
||||
uint8(34): 100,
|
||||
uint8(35): 100,
|
||||
uint8(36): 50,
|
||||
|
@ -110,25 +99,27 @@ class TestPotIterations:
|
|||
total_space = sum(farmer_space.values())
|
||||
percentage_space = {k: float(sp / total_space) for k, sp in farmer_space.items()}
|
||||
wins = {k: 0 for k in farmer_ks.keys()}
|
||||
total_slots = 500
|
||||
sub_slot_iters = uint64(100000000)
|
||||
total_slots = 200
|
||||
num_sps = 16
|
||||
sp_interval_iters = uint64(100000000 // 32)
|
||||
difficulty = uint64(500000000000)
|
||||
|
||||
for slot_index in range(total_slots):
|
||||
total_wins_in_slot = 0
|
||||
for k, count in farmer_ks.items():
|
||||
for farmer_index in range(count):
|
||||
quality = std_hash(slot_index.to_bytes(32, "big") + bytes(farmer_index))
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality,
|
||||
k,
|
||||
difficulty,
|
||||
)
|
||||
if required_iters < sub_slot_iters:
|
||||
wins[k] += 1
|
||||
total_wins_in_slot += 1
|
||||
for sp_index in range(num_sps):
|
||||
sp_hash = std_hash(slot_index.to_bytes(4, "big") + sp_index.to_bytes(4, "big"))
|
||||
for k, count in farmer_ks.items():
|
||||
for farmer_index in range(count):
|
||||
quality = std_hash(slot_index.to_bytes(4, "big") + k.to_bytes(1, "big") + bytes(farmer_index))
|
||||
required_iters = calculate_iterations_quality(quality, k, difficulty, sp_hash)
|
||||
if required_iters < sp_interval_iters:
|
||||
wins[k] += 1
|
||||
total_wins_in_slot += 1
|
||||
print(total_wins_in_slot)
|
||||
|
||||
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
|
||||
|
|
|
@ -94,7 +94,7 @@ class TestGenesisBlock:
|
|||
class TestAddingMoreBlocks:
|
||||
@pytest.mark.asyncio
|
||||
async def test_basic_chain(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 200)
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 2000)
|
||||
for block in blocks:
|
||||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
|
@ -138,7 +138,7 @@ class TestAddingMoreBlocks:
|
|||
blocks = bt.get_consecutive_blocks(test_constants, 10, skip_slots=10, block_list=blocks)
|
||||
for block in blocks[10:]:
|
||||
result, err, _ = await blockchain.receive_block(block)
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
assert err is None
|
||||
assert blockchain.get_peak().height == 19
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -181,6 +181,7 @@ class TestAddingMoreBlocks:
|
|||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
print(f"added {block.height} {block.total_iters}")
|
||||
assert empty_blockchain.get_peak().height == len(blocks) - 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
@ -27,11 +27,11 @@ from tests.time_out_assert import time_out_assert
|
|||
|
||||
test_constants = constants.replace(
|
||||
**{
|
||||
"DIFFICULTY_STARTING": 1,
|
||||
"DIFFICULTY_STARTING": 2 ** 8,
|
||||
"DISCRIMINANT_SIZE_BITS": 32,
|
||||
"SUB_EPOCH_SUB_BLOCKS": 70,
|
||||
"EPOCH_SUB_BLOCKS": 140,
|
||||
"IPS_STARTING": 10 * 1,
|
||||
"IPS_STARTING": 2 ** 9,
|
||||
"NUMBER_ZERO_BITS_PLOT_FILTER": 1, # H(plot signature of the challenge) must start with these many zeroes
|
||||
"NUMBER_ZERO_BITS_SP_FILTER": 1, # H(plot signature of the challenge) must start with these many zeroes
|
||||
"MAX_FUTURE_TIME": 3600
|
||||
|
|
|
@ -2,7 +2,7 @@ from secrets import token_bytes
|
|||
from blspy import AugSchemeMPL
|
||||
from src.types.proof_of_space import ProofOfSpace # pylint: disable=E0401
|
||||
|
||||
# from src.consensus.default_constants import DEFAULT_CONSTANTS
|
||||
from src.consensus.default_constants import DEFAULT_CONSTANTS
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@ class TestProofOfSpace:
|
|||
"""
|
||||
num_trials = 40000
|
||||
success_count = 0
|
||||
target_filter = (2 ** constants.NUMBER_ZERO_BITS_PLOT_FILTER) * (2 ** constants.NUMBER_ZERO_BITS_SP_FILTER)
|
||||
target_filter = (2 ** DEFAULT_CONSTANTS.NUMBER_ZERO_BITS_SP_FILTER) * (2 ** constants.NUMBER_ZERO_BITS_SP_FILTER)
|
||||
sk = AugSchemeMPL.key_gen(bytes([0x44] * 32))
|
||||
sig = AugSchemeMPL.sign(sk, b"")
|
||||
for _ in range(num_trials):
|
||||
|
|
Loading…
Reference in New Issue