More difficulty fixes and tests
This commit is contained in:
parent
3f064e529c
commit
069535e52e
|
@ -175,27 +175,30 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_ICC_EOS_VDF)
|
||||
|
||||
# 2g. Check infused challenge sub-slot hash in challenge sub-slot
|
||||
if sub_slot.reward_chain.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
|
||||
# 2g. Check infused challenge sub-slot hash in challenge chain, deficit 5
|
||||
if (
|
||||
sub_slot.infused_challenge_chain.get_hash()
|
||||
!= sub_slot.challenge_chain.infused_challenge_chain_sub_slot_hash
|
||||
):
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_CC)
|
||||
else:
|
||||
# 2h. Check infused challenge sub-slot hash not included for other deficits
|
||||
if sub_slot.challenge_chain.infused_challenge_chain_sub_slot_hash is not None:
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_CC)
|
||||
|
||||
# 2h. Check infused challenge sub-slot hash in reward sub-slot
|
||||
# 2i. 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
|
||||
):
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_RC)
|
||||
else:
|
||||
assert sub_slot.infused_challenge_chain is None
|
||||
# 2j. If no icc, check that the cc doesn't include it
|
||||
if sub_slot.challenge_chain.infused_challenge_chain_sub_slot_hash is not None:
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_CC)
|
||||
|
||||
# 2k. If no icc, check that the cc doesn't include it
|
||||
if sub_slot.reward_chain.infused_challenge_chain_sub_slot_hash is not None:
|
||||
return None, ValidationError(Err.INVALID_ICC_HASH_RC)
|
||||
|
||||
|
@ -203,31 +206,30 @@ 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
|
||||
|
||||
# 2i. check sub-epoch summary hash is None for empty slots
|
||||
# 2l. 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)
|
||||
|
||||
# 2j. Check new difficulty
|
||||
if finishes_epoch:
|
||||
# 2m. Check new difficulty and ips
|
||||
if sub_slot.challenge_chain.new_ips != ips:
|
||||
return None, ValidationError(Err.INVALID_NEW_IPS)
|
||||
if sub_slot.challenge_chain.new_difficulty != difficulty:
|
||||
return None, ValidationError(Err.INVALID_NEW_DIFFICULTY)
|
||||
else:
|
||||
# 2n. Check new difficulty and ips are None if we don't finish epoch
|
||||
if sub_slot.challenge_chain.new_ips is not None:
|
||||
return None, ValidationError(Err.INVALID_NEW_IPS)
|
||||
if sub_slot.challenge_chain.new_difficulty is not None:
|
||||
return None, ValidationError(Err.INVALID_NEW_DIFFICULTY)
|
||||
|
||||
# 2k. Check challenge sub-slot hash in reward sub-slot
|
||||
# 2o. Check challenge sub-slot hash in reward sub-slot
|
||||
if sub_slot.challenge_chain.get_hash() != sub_slot.reward_chain.challenge_chain_sub_slot_hash:
|
||||
return None, ValidationError(
|
||||
Err.INVALID_CHALLENGE_SLOT_HASH_RC, "sub-slot hash in reward sub-slot mismatch"
|
||||
)
|
||||
|
||||
# 2l. Check challenge chain sub-slot VDF
|
||||
# 2m. 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()
|
||||
|
@ -256,6 +258,7 @@ async def validate_unfinished_header_block(
|
|||
finished_sub_slot_n - 1
|
||||
].reward_chain.get_hash()
|
||||
|
||||
# 2p. Check end of reward slot VDF
|
||||
target_vdf_info = VDFInfo(
|
||||
rc_eos_vdf_challenge,
|
||||
ClassgroupElement.get_default_element(), # Reward chain always infuses at previous sub-block
|
||||
|
@ -267,6 +270,7 @@ async def validate_unfinished_header_block(
|
|||
):
|
||||
return None, ValidationError(Err.INVALID_RC_EOS_VDF)
|
||||
|
||||
# 2q. Check challenge chain sub-slot VDF
|
||||
partial_cc_vdf_info = VDFInfo(
|
||||
cc_eos_vdf_challenge,
|
||||
cc_start_element,
|
||||
|
@ -284,15 +288,15 @@ 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)
|
||||
|
||||
# 2n. Check deficit (5 deficit edge case for genesis block)
|
||||
if genesis_block:
|
||||
# 2r. Check deficit (5 deficit edge case for genesis block)
|
||||
if sub_slot.reward_chain.deficit != constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
|
||||
return None, ValidationError(
|
||||
Err.INVALID_DEFICIT, f"genesis, expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}"
|
||||
)
|
||||
else:
|
||||
if prev_sb.deficit == 0:
|
||||
# If there is a challenge chain infusion, resets deficit to 5
|
||||
# 2s. If prev sb had deficit 0, resets deficit to 5
|
||||
if sub_slot.reward_chain.deficit != constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
|
||||
log.error(
|
||||
constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK,
|
||||
|
@ -302,7 +306,7 @@ async def validate_unfinished_header_block(
|
|||
f"expected deficit {constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK}",
|
||||
)
|
||||
else:
|
||||
# Otherwise, deficit stays the same at the slot ends, cannot reset until 0
|
||||
# 2t. Otherwise, deficit stays the same at the slot ends, cannot reset until 0
|
||||
if sub_slot.reward_chain.deficit != prev_sb.deficit:
|
||||
return None, ValidationError(Err.INVALID_DEFICIT, "deficit is wrong at slot end")
|
||||
|
||||
|
@ -311,7 +315,7 @@ async def validate_unfinished_header_block(
|
|||
if ses_hash is not None:
|
||||
# 3a. Check that genesis block does not have sub-epoch summary
|
||||
if genesis_block:
|
||||
return None, ValidationError(Err.INVALID_SUB_EPOCH_SUMMARY, "genesis with sub-epoch-summary hash")
|
||||
return None, ValidationError(Err.INVALID_SUB_EPOCH_SUMMARY_HASH, "genesis with sub-epoch-summary hash")
|
||||
|
||||
# 3b. Check that we finished a slot and we finished a sub-epoch
|
||||
if not new_sub_slot or not finishes_se:
|
||||
|
|
|
@ -20,7 +20,6 @@ class ConsensusConstants:
|
|||
SIGNIFICANT_BITS: int # The number of bits to look at in difficulty and min iters. The rest are zeroed
|
||||
DISCRIMINANT_SIZE_BITS: int # Max is 1024 (based on ClassGroupElement int size)
|
||||
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
|
||||
NUM_SP_INTERVALS_EXTRA: int
|
||||
MAX_FUTURE_TIME: int # The next block can have a timestamp of at most these many seconds more
|
||||
|
|
|
@ -19,7 +19,6 @@ testnet_kwargs = {
|
|||
"SIGNIFICANT_BITS": 12, # The number of bits to look at in difficulty and min iters. The rest are zeroed
|
||||
"DISCRIMINANT_SIZE_BITS": 1024, # Max is 1024 (based on ClassGroupElement int size)
|
||||
"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
|
||||
"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
|
||||
|
|
|
@ -29,12 +29,12 @@ def full_block_to_sub_block_record(
|
|||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
prev_sb.header_hash,
|
||||
prev_sb.prev_hash,
|
||||
prev_sb.height,
|
||||
prev_sb.ips,
|
||||
prev_sb.deficit,
|
||||
len(block.finished_sub_slots) > 0,
|
||||
prev_sb.total_iters,
|
||||
prev_sb.sp_total_iters(constants),
|
||||
)
|
||||
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)
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
from typing import Dict, Optional, Union
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_ip_iters, calculate_sp_iters, is_overflow_sub_block
|
||||
from src.consensus.pot_iterations import (
|
||||
calculate_ip_iters,
|
||||
calculate_sp_iters,
|
||||
is_overflow_sub_block,
|
||||
calculate_sub_slot_iters,
|
||||
)
|
||||
from src.consensus.deficit import calculate_deficit
|
||||
from src.consensus.difficulty_adjustment import get_next_ips, finishes_sub_epoch, get_next_difficulty
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
|
@ -60,11 +65,12 @@ def next_sub_epoch_summary(
|
|||
ips = constants.IPS_STARTING
|
||||
else:
|
||||
assert prev_sb is not None
|
||||
# This is the ips of the current block
|
||||
ips = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
block.prev_header_hash,
|
||||
prev_sb.prev_hash,
|
||||
prev_sb.height,
|
||||
prev_sb.ips,
|
||||
prev_sb.deficit,
|
||||
|
@ -85,28 +91,30 @@ def next_sub_epoch_summary(
|
|||
if finishes_epoch:
|
||||
sp_iters = calculate_sp_iters(constants, ips, signage_point_index)
|
||||
ip_iters = calculate_ip_iters(constants, ips, signage_point_index, required_iters)
|
||||
sub_slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
next_difficulty = get_next_difficulty(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
block.header_hash,
|
||||
block.prev_header_hash,
|
||||
block.height,
|
||||
uint64(block.weight - prev_sb.weight),
|
||||
deficit,
|
||||
True,
|
||||
uint128(block.total_iters - ip_iters + sp_iters),
|
||||
uint128(block.total_iters - ip_iters + sp_iters - (sub_slot_iters if overflow else 0)),
|
||||
)
|
||||
next_ips = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
block.header_hash,
|
||||
block.prev_header_hash,
|
||||
block.height,
|
||||
ips,
|
||||
deficit,
|
||||
True,
|
||||
uint128(block.total_iters - ip_iters + sp_iters),
|
||||
uint128(block.total_iters - ip_iters + sp_iters - (sub_slot_iters if overflow else 0)),
|
||||
)
|
||||
sp_total_iters = uint128(block.total_iters - ip_iters + sp_iters - (sub_slot_iters if overflow else 0))
|
||||
else:
|
||||
next_difficulty = None
|
||||
next_ips = None
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from typing import Dict, Optional, List
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.header import Header
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.types.unfinished_header_block import UnfinishedHeaderBlock
|
||||
from src.util.ints import uint32, uint64
|
||||
from src.types.coin_record import CoinRecord
|
||||
from src.rpc.rpc_client import RpcClient
|
||||
|
@ -18,38 +19,35 @@ class FullNodeRpcClient(RpcClient):
|
|||
|
||||
async def get_blockchain_state(self) -> Dict:
|
||||
response = await self.fetch("get_blockchain_state", {})
|
||||
response["blockchain_state"]["tips"] = [
|
||||
Header.from_json_dict(tip) for tip in response["blockchain_state"]["tips"]
|
||||
]
|
||||
response["blockchain_state"]["lca"] = Header.from_json_dict(response["blockchain_state"]["lca"])
|
||||
response["blockchain_state"]["peak"] = SubBlockRecord.from_json_dict(response["blockchain_state"]["peak"])
|
||||
return response["blockchain_state"]
|
||||
|
||||
async def get_block(self, header_hash) -> Optional[FullBlock]:
|
||||
async def get_sub_block(self, header_hash) -> Optional[FullBlock]:
|
||||
try:
|
||||
response = await self.fetch("get_block", {"header_hash": header_hash.hex()})
|
||||
response = await self.fetch("get_sub_block", {"header_hash": header_hash.hex()})
|
||||
except Exception:
|
||||
return None
|
||||
return FullBlock.from_json_dict(response["sub_block"])
|
||||
|
||||
async def get_header_by_height(self, header_height) -> Optional[Header]:
|
||||
async def get_sub_block_record_by_height(self, header_height) -> Optional[SubBlockRecord]:
|
||||
try:
|
||||
response = await self.fetch("get_header_by_height", {"height": header_height})
|
||||
response = await self.fetch("get_sub_block_record_by_height", {"height": header_height})
|
||||
except Exception:
|
||||
return None
|
||||
return Header.from_json_dict(response["header"])
|
||||
return SubBlockRecord.from_json_dict(response["sub_block_record"])
|
||||
|
||||
async def get_header(self, header_hash) -> Optional[Header]:
|
||||
async def get_sub_block_record(self, header_hash) -> Optional[SubBlockRecord]:
|
||||
try:
|
||||
response = await self.fetch("get_header", {"header_hash": header_hash.hex()})
|
||||
if response["header"] is None:
|
||||
response = await self.fetch("get_sub_block_record", {"header_hash": header_hash.hex()})
|
||||
if response["sub_block_record"] is None:
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
return Header.from_json_dict(response["header"])
|
||||
return SubBlockRecord.from_json_dict(response["sub_block_record"])
|
||||
|
||||
async def get_unfinished_block_headers(self, height: uint32) -> List[Header]:
|
||||
response = await self.fetch("get_unfinished_block_headers", {"height": height})
|
||||
return [Header.from_json_dict(r) for r in response["headers"]]
|
||||
async def get_unfinished_sub_block_header_blocks(self, height: uint32) -> List[UnfinishedHeaderBlock]:
|
||||
response = await self.fetch("get_unfinished_sub_block_header_blocks", {"height": height})
|
||||
return [UnfinishedHeaderBlock.from_json_dict(r) for r in response["s"]]
|
||||
|
||||
async def get_network_space(self, newer_block_header_hash: str, older_block_header_hash: str) -> Optional[uint64]:
|
||||
try:
|
||||
|
@ -73,6 +71,6 @@ class FullNodeRpcClient(RpcClient):
|
|||
CoinRecord.from_json_dict(coin) for coin in ((await self.fetch("get_unspent_coins", d))["coin_records"])
|
||||
]
|
||||
|
||||
async def get_heaviest_block_seen(self) -> Header:
|
||||
async def get_heaviest_block_seen(self) -> SubBlockRecord:
|
||||
response = await self.fetch("get_heaviest_block_seen", {})
|
||||
return Header.from_json_dict(response["tip"])
|
||||
return SubBlockRecord.from_json_dict(response["peak"])
|
||||
|
|
|
@ -9,7 +9,6 @@ test_constants = constants.replace(
|
|||
"MEMPOOL_BLOCK_BUFFER": 10,
|
||||
"IPS_STARTING": 10 * 1,
|
||||
"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
|
||||
"CLVM_COST_RATIO_CONSTANT": 108,
|
||||
"COINBASE_FREEZE_PERIOD": 0,
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ class BlockTools:
|
|||
mkdir(temp_dir)
|
||||
args = Namespace()
|
||||
# Can't go much lower than 18, since plots start having no solutions
|
||||
args.size = 18
|
||||
args.size = 19
|
||||
# Uses many plots for testing, in order to guarantee proofs of space at every height
|
||||
args.num = 320
|
||||
args.buffer = 100
|
||||
|
@ -202,7 +202,7 @@ class BlockTools:
|
|||
) -> List[FullBlock]:
|
||||
transaction_data_included = False
|
||||
if time_per_sub_block is None:
|
||||
time_per_sub_block = constants.SLOT_TIME_TARGET / constants.SLOT_SUB_BLOCKS_TARGET
|
||||
time_per_sub_block: float = float(constants.SLOT_TIME_TARGET) / float(constants.SLOT_SUB_BLOCKS_TARGET)
|
||||
|
||||
if farmer_reward_puzzle_hash is None:
|
||||
farmer_reward_puzzle_hash = self.farmer_ph
|
||||
|
@ -848,14 +848,14 @@ def finish_sub_block(
|
|||
slot_iters = calculate_sub_slot_iters(constants, ips)
|
||||
if len(finished_sub_slots) == 0:
|
||||
new_ip_iters = unfinished_block.total_iters - latest_sub_block.total_iters
|
||||
if new_ip_iters < 0:
|
||||
print("Here.")
|
||||
cc_vdf_input = latest_sub_block.challenge_vdf_output
|
||||
rc_vdf_challenge = latest_sub_block.reward_infusion_new_challenge
|
||||
else:
|
||||
new_ip_iters = ip_iters
|
||||
cc_vdf_input = ClassgroupElement.get_default_element()
|
||||
rc_vdf_challenge = slot_rc_challenge
|
||||
if new_ip_iters < 0:
|
||||
print("Bad here")
|
||||
cc_ip_vdf, cc_ip_proof = get_vdf_info_and_proof(
|
||||
constants,
|
||||
cc_vdf_input,
|
||||
|
|
|
@ -13,6 +13,7 @@ from src.types.slots import InfusedChallengeChainSubSlot
|
|||
from src.types.vdf import VDFInfo, VDFProof
|
||||
from src.util.block_tools import get_vdf_info_and_proof
|
||||
from src.util.errors import Err
|
||||
from src.util.hash import std_hash
|
||||
from src.util.ints import uint64, uint8, int512
|
||||
from tests.recursive_replace import recursive_replace
|
||||
from tests.setup_nodes import test_constants, bt
|
||||
|
@ -94,9 +95,35 @@ class TestGenesisBlock:
|
|||
|
||||
class TestAddingMoreBlocks:
|
||||
@pytest.mark.asyncio
|
||||
async def test_basic_chain(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 400)
|
||||
async def test_long_chain(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 200)
|
||||
for block in blocks:
|
||||
if (
|
||||
len(block.finished_sub_slots) > 0
|
||||
and block.finished_sub_slots[0].challenge_chain.subepoch_summary_hash is not None
|
||||
):
|
||||
# Sub/Epoch. Try using a bad ips and difficulty to test 2m and 2n
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[0],
|
||||
"challenge_chain.new_ips",
|
||||
uint64(10000000),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_NEW_IPS
|
||||
new_finished_ss_2 = recursive_replace(
|
||||
block.finished_sub_slots[0],
|
||||
"challenge_chain.new_difficulty",
|
||||
uint64(10000000),
|
||||
)
|
||||
block_bad_2 = recursive_replace(
|
||||
block, "finished_sub_slots", [new_finished_ss_2] + block.finished_sub_slots[1:]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_2)
|
||||
assert err == Err.INVALID_NEW_DIFFICULTY
|
||||
|
||||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
@ -301,8 +328,7 @@ class TestAddingMoreBlocks:
|
|||
assert err == Err.SHOULD_NOT_HAVE_ICC
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_icc_sub_slot_vdf_iters(self, empty_blockchain):
|
||||
blockchain = empty_blockchain
|
||||
async def test_invalid_icc_sub_slot_vdf(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 10)
|
||||
for block in blocks:
|
||||
if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None:
|
||||
|
@ -395,13 +421,381 @@ class TestAddingMoreBlocks:
|
|||
assert err == Err.INVALID_ICC_EOS_VDF
|
||||
|
||||
else:
|
||||
result, err, _ = await blockchain.receive_block(block)
|
||||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_icc_into_cc(self, empty_blockchain):
|
||||
pass
|
||||
blockchain = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1)
|
||||
case_1, case_2 = False, False
|
||||
while not case_1 or not case_2:
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1, block_list=blocks, skip_slots=1)
|
||||
block = blocks[-1]
|
||||
if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None:
|
||||
if (
|
||||
block.finished_sub_slots[-1].reward_chain.deficit
|
||||
== test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK
|
||||
):
|
||||
# 2g
|
||||
print("2g test")
|
||||
case_1 = True
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
infused_challenge_chain_sub_slot_hash=bytes([1] * 32),
|
||||
),
|
||||
)
|
||||
else:
|
||||
# 2h
|
||||
print("2h test")
|
||||
case_2 = True
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[
|
||||
-1
|
||||
].infused_challenge_chain.get_hash(),
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
result, err, _ = await blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_ICC_HASH_CC
|
||||
|
||||
# 2i
|
||||
new_finished_ss_bad_rc = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
replace(block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=None),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
|
||||
)
|
||||
result, err, _ = await blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_ICC_HASH_RC
|
||||
elif len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is None:
|
||||
# 2j
|
||||
new_finished_ss_bad_cc = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
infused_challenge_chain_sub_slot_hash=bytes([1] * 32),
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_cc]
|
||||
)
|
||||
print(len(block.finished_sub_slots))
|
||||
result, err, _ = await blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_ICC_HASH_CC
|
||||
|
||||
# 2k
|
||||
new_finished_ss_bad_rc = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
replace(
|
||||
block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=bytes([1] * 32)
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
|
||||
)
|
||||
result, err, _ = await blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_ICC_HASH_RC
|
||||
|
||||
# Finally, add the block properly
|
||||
result, err, _ = await blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_slot_no_ses(self, empty_blockchain):
|
||||
# 2l
|
||||
blockchain = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1)
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1, block_list=blocks, skip_slots=4)
|
||||
|
||||
new_finished_ss = recursive_replace(
|
||||
blocks[-1].finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
replace(blocks[-1].finished_sub_slots[-1].challenge_chain, subepoch_summary_hash=std_hash(b"0")),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
result, err, _ = await blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wrong_cc_hash_rc(self, empty_blockchain):
|
||||
# 2o
|
||||
blockchain = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1, skip_slots=1)
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1, skip_slots=1, block_list=blocks)
|
||||
assert (await blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
new_finished_ss = recursive_replace(
|
||||
blocks[-1].finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
replace(blocks[-1].finished_sub_slots[-1].reward_chain, challenge_chain_sub_slot_hash=bytes([3] * 32)),
|
||||
)
|
||||
block_1_bad = recursive_replace(
|
||||
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
|
||||
result, err, _ = await blockchain.receive_block(block_1_bad)
|
||||
assert result == ReceiveBlockResult.INVALID_BLOCK
|
||||
assert err == Err.INVALID_CHALLENGE_SLOT_HASH_RC
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_cc_sub_slot_vdf(self, empty_blockchain):
|
||||
# 2q
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 10)
|
||||
for block in blocks:
|
||||
if len(block.finished_sub_slots):
|
||||
# Bad iters
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
"challenge_chain_end_of_slot_vdf.number_of_iterations",
|
||||
uint64(10000000),
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_CC_EOS_VDF
|
||||
|
||||
# Bad output
|
||||
new_finished_ss_2 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
"challenge_chain_end_of_slot_vdf.output",
|
||||
ClassgroupElement.get_default_element(),
|
||||
),
|
||||
)
|
||||
block_bad_2 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_2)
|
||||
assert err == Err.INVALID_CC_EOS_VDF
|
||||
|
||||
# Bad challenge hash
|
||||
new_finished_ss_3 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
"challenge_chain_end_of_slot_vdf.challenge_hash",
|
||||
bytes([1] * 32),
|
||||
),
|
||||
)
|
||||
block_bad_3 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_3)
|
||||
assert err == Err.INVALID_CC_EOS_VDF
|
||||
|
||||
# Bad input
|
||||
new_finished_ss_4 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"challenge_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].challenge_chain,
|
||||
"challenge_chain_end_of_slot_vdf.input",
|
||||
ClassgroupElement(int512(5), int512(1)),
|
||||
),
|
||||
)
|
||||
block_bad_4 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_4]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_4)
|
||||
assert err == Err.INVALID_CC_EOS_VDF
|
||||
|
||||
# Bad proof
|
||||
new_finished_ss_5 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"proofs.challenge_chain_slot_proof",
|
||||
VDFProof(uint8(0), b"1239819023890"),
|
||||
)
|
||||
block_bad_5 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_5)
|
||||
assert err == Err.INVALID_CC_EOS_VDF
|
||||
|
||||
else:
|
||||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_rc_sub_slot_vdf(self, empty_blockchain):
|
||||
# 2p
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 10)
|
||||
for block in blocks:
|
||||
if len(block.finished_sub_slots):
|
||||
# Bad iters
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].reward_chain,
|
||||
"reward_chain_end_of_slot_vdf.number_of_iterations",
|
||||
uint64(10000000),
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_RC_EOS_VDF
|
||||
|
||||
# Bad output
|
||||
new_finished_ss_2 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].reward_chain,
|
||||
"reward_chain_end_of_slot_vdf.output",
|
||||
ClassgroupElement.get_default_element(),
|
||||
),
|
||||
)
|
||||
block_bad_2 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_2)
|
||||
assert err == Err.INVALID_RC_EOS_VDF
|
||||
|
||||
# Bad challenge hash
|
||||
new_finished_ss_3 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].reward_chain,
|
||||
"end_of_slot_vdf.challenge_hash",
|
||||
bytes([1] * 32),
|
||||
),
|
||||
)
|
||||
block_bad_3 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_3)
|
||||
assert err == Err.INVALID_RC_EOS_VDF
|
||||
|
||||
# Bad input
|
||||
new_finished_ss_4 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].reward_chain,
|
||||
"end_of_slot_vdf.input",
|
||||
ClassgroupElement(int512(5), int512(1)),
|
||||
),
|
||||
)
|
||||
block_bad_4 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_4]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_4)
|
||||
assert err == Err.INVALID_RC_EOS_VDF
|
||||
|
||||
# Bad proof
|
||||
new_finished_ss_5 = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"proofs.reward_chain_slot_proof",
|
||||
VDFProof(uint8(0), b"1239819023890"),
|
||||
)
|
||||
block_bad_5 = recursive_replace(
|
||||
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad_5)
|
||||
assert err == Err.INVALID_RC_EOS_VDF
|
||||
|
||||
else:
|
||||
result, err, _ = await empty_blockchain.receive_block(block)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_genesis_bad_deficit(self, empty_blockchain):
|
||||
# 2r
|
||||
block = bt.get_consecutive_blocks(test_constants, 1, skip_slots=2)[0]
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[-1].reward_chain,
|
||||
"deficit",
|
||||
test_constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1,
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss])
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_DEFICIT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset_deficit(self, empty_blockchain):
|
||||
# 2s, 2t
|
||||
blockchain = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 2)
|
||||
await empty_blockchain.receive_block(blocks[0])
|
||||
await empty_blockchain.receive_block(blocks[1])
|
||||
case_1, case_2 = False, False
|
||||
while not case_1 or not case_2:
|
||||
blocks = bt.get_consecutive_blocks(test_constants, 1, block_list=blocks, skip_slots=1)
|
||||
if len(blocks[-1].finished_sub_slots) > 0:
|
||||
new_finished_ss = recursive_replace(
|
||||
blocks[-1].finished_sub_slots[-1],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
blocks[-1].finished_sub_slots[-1].reward_chain,
|
||||
"deficit",
|
||||
uint8(0),
|
||||
),
|
||||
)
|
||||
if blockchain.sub_blocks[blocks[-2].header_hash].deficit == 0:
|
||||
case_1 = True
|
||||
else:
|
||||
case_2 = True
|
||||
|
||||
block_bad = recursive_replace(
|
||||
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
|
||||
)
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_DEFICIT or err == Err.INVALID_ICC_HASH_CC
|
||||
|
||||
result, err, _ = await empty_blockchain.receive_block(blocks[-1])
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_genesis_has_ses(self, empty_blockchain):
|
||||
# 3a
|
||||
block = bt.get_consecutive_blocks(test_constants, 1, skip_slots=2)[0]
|
||||
new_finished_ss = recursive_replace(
|
||||
block.finished_sub_slots[0],
|
||||
"reward_chain",
|
||||
recursive_replace(
|
||||
block.finished_sub_slots[0].challenge_chain,
|
||||
"subepoch_summary_hash",
|
||||
bytes([0] * 32),
|
||||
),
|
||||
)
|
||||
block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:])
|
||||
result, err, _ = await empty_blockchain.receive_block(block_bad)
|
||||
assert err == Err.INVALID_SUB_EPOCH_SUMMARY_HASH
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -27,13 +27,12 @@ from tests.time_out_assert import time_out_assert
|
|||
|
||||
test_constants = constants.replace(
|
||||
**{
|
||||
"DIFFICULTY_STARTING": 2 ** 3,
|
||||
"DIFFICULTY_STARTING": 2 ** 5,
|
||||
"DISCRIMINANT_SIZE_BITS": 32,
|
||||
"SUB_EPOCH_SUB_BLOCKS": 70,
|
||||
"EPOCH_SUB_BLOCKS": 140,
|
||||
"IPS_STARTING": 2 ** 8,
|
||||
"IPS_STARTING": 2 ** 7,
|
||||
"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
|
||||
* 24
|
||||
* 10, # Allows creating blockchains with timestamps up to 10 days in the future, for testing
|
||||
|
|
|
@ -13,7 +13,6 @@ test_constants = constants.replace(
|
|||
"DIFFICULTY_STARTING": 1000,
|
||||
"MIN_ITERS_STARTING": 100000,
|
||||
"NUMBER_ZERO_BITS_PLOT_FILTER": 1,
|
||||
"NUMBER_ZERO_BITS_SP_FILTER": 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue