mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-11-10 12:29:49 +03:00
parent
c5595e280c
commit
e55988479b
@ -112,7 +112,7 @@ async def print_balances(args: dict, wallet_client: WalletRpcClient, fingerprint
|
||||
if typ != "STANDARD_WALLET":
|
||||
print(f"Wallet ID {wallet_id} type {typ} {summary['name']}")
|
||||
print(f" -Confirmed: " f"{balances['confirmed_wallet_balance']/units['colouredcoin']}")
|
||||
print(f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}")
|
||||
print(f" -Confirmed - Pending Outgoing: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}")
|
||||
print(f" -Spendable: {balances['spendable_balance']/units['colouredcoin']}")
|
||||
print(f" -Pending change: {balances['pending_change']/units['colouredcoin']}")
|
||||
else:
|
||||
|
@ -244,6 +244,7 @@ async def validate_block_body(
|
||||
additions_dic: Dict[bytes32, Coin] = {}
|
||||
# 10. Check additions for max coin amount
|
||||
# Be careful to check for 64 bit overflows in other languages. This is the max 64 bit unsigned integer
|
||||
# We will not even reach here because Coins do type checking (uint64)
|
||||
for coin in additions + coinbase_additions:
|
||||
additions_dic[coin.name()] = coin
|
||||
if coin.amount > constants.MAX_COIN_AMOUNT:
|
||||
@ -287,6 +288,7 @@ async def validate_block_body(
|
||||
return Err.DOUBLE_SPEND, None
|
||||
|
||||
# 15. Check if removals exist and were not previously spent. (unspent_db + diff_store + this_block)
|
||||
# The fork point is the last block in common between the peak chain and the chain of `block`
|
||||
if peak is None or height == 0:
|
||||
fork_h: int = -1
|
||||
elif fork_point_with_peak is not None:
|
||||
@ -294,30 +296,27 @@ async def validate_block_body(
|
||||
else:
|
||||
fork_h = find_fork_point_in_chain(blocks, peak, blocks.block_record(block.prev_header_hash))
|
||||
|
||||
if fork_h == -1:
|
||||
coin_store_reorg_height = -1
|
||||
else:
|
||||
last_block_in_common = await blocks.get_block_record_from_db(blocks.height_to_hash(uint32(fork_h)))
|
||||
assert last_block_in_common is not None
|
||||
coin_store_reorg_height = last_block_in_common.height
|
||||
|
||||
# Get additions and removals since (after) fork_h but not including this block
|
||||
additions_since_fork: Dict[bytes32, Tuple[Coin, uint32]] = {}
|
||||
# The values include: the coin that was added, the height of the block in which it was confirmed, and the
|
||||
# timestamp of the block in which it was confirmed
|
||||
additions_since_fork: Dict[bytes32, Tuple[Coin, uint32, uint64]] = {} # This includes coinbase additions
|
||||
removals_since_fork: Set[bytes32] = set()
|
||||
coinbases_since_fork: Dict[bytes32, uint32] = {}
|
||||
|
||||
# For height 0, there are no additions and removals before this block, so we can skip
|
||||
if height > 0:
|
||||
# First, get all the blocks in the fork > fork_h, < block.height
|
||||
prev_block: Optional[FullBlock] = await block_store.get_full_block(block.prev_header_hash)
|
||||
reorg_blocks: Dict[int, FullBlock] = {}
|
||||
reorg_blocks: Dict[uint32, FullBlock] = {}
|
||||
curr: Optional[FullBlock] = prev_block
|
||||
assert curr is not None
|
||||
reorg_blocks[curr.height] = curr
|
||||
while curr.height > fork_h:
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = await block_store.get_full_block(curr.prev_header_hash)
|
||||
assert curr is not None
|
||||
reorg_blocks[curr.height] = curr
|
||||
if fork_h != -1:
|
||||
assert len(reorg_blocks) == height - fork_h - 1
|
||||
|
||||
curr = prev_block
|
||||
assert curr is not None
|
||||
@ -326,9 +325,9 @@ async def validate_block_body(
|
||||
if curr.transactions_generator is not None:
|
||||
# These blocks are in the past and therefore assumed to be valid, so get_block_generator won't raise
|
||||
curr_block_generator: Optional[BlockGenerator] = await get_block_generator(curr)
|
||||
assert curr_block_generator is not None
|
||||
assert curr_block_generator is not None and curr.transactions_info is not None
|
||||
curr_npc_result = get_name_puzzle_conditions(
|
||||
curr_block_generator, constants.MAX_BLOCK_COST_CLVM, False
|
||||
curr_block_generator, min(constants.MAX_BLOCK_COST_CLVM, curr.transactions_info.cost), False
|
||||
)
|
||||
removals_in_curr, additions_in_curr = tx_removals_and_additions(curr_npc_result.npc_list)
|
||||
else:
|
||||
@ -336,13 +335,21 @@ async def validate_block_body(
|
||||
additions_in_curr = []
|
||||
|
||||
for c_name in removals_in_curr:
|
||||
assert c_name not in removals_since_fork
|
||||
removals_since_fork.add(c_name)
|
||||
for c in additions_in_curr:
|
||||
additions_since_fork[c.name()] = (c, curr.height)
|
||||
assert c.name() not in additions_since_fork
|
||||
assert curr.foliage_transaction_block is not None
|
||||
additions_since_fork[c.name()] = (c, curr.height, curr.foliage_transaction_block.timestamp)
|
||||
|
||||
for coinbase_coin in curr.get_included_reward_coins():
|
||||
additions_since_fork[coinbase_coin.name()] = (coinbase_coin, curr.height)
|
||||
coinbases_since_fork[coinbase_coin.name()] = curr.height
|
||||
assert coinbase_coin.name() not in additions_since_fork
|
||||
assert curr.foliage_transaction_block is not None
|
||||
additions_since_fork[coinbase_coin.name()] = (
|
||||
coinbase_coin,
|
||||
curr.height,
|
||||
curr.foliage_transaction_block.timestamp,
|
||||
)
|
||||
if curr.height == 0:
|
||||
break
|
||||
curr = reorg_blocks[curr.height - 1]
|
||||
@ -356,18 +363,18 @@ async def validate_block_body(
|
||||
new_unspent: CoinRecord = CoinRecord(
|
||||
rem_coin,
|
||||
height,
|
||||
uint32(0),
|
||||
height,
|
||||
True,
|
||||
False,
|
||||
(rem in coinbases_since_fork),
|
||||
block.foliage_transaction_block.timestamp,
|
||||
)
|
||||
removal_coin_records[new_unspent.name] = new_unspent
|
||||
else:
|
||||
unspent = await coin_store.get_coin_record(rem)
|
||||
if unspent is not None and unspent.confirmed_block_index <= coin_store_reorg_height:
|
||||
if unspent is not None and unspent.confirmed_block_index <= fork_h:
|
||||
# Spending something in the current chain, confirmed before fork
|
||||
# (We ignore all coins confirmed after fork)
|
||||
if unspent.spent == 1 and unspent.spent_block_index <= coin_store_reorg_height:
|
||||
if unspent.spent == 1 and unspent.spent_block_index <= fork_h:
|
||||
# Check for coins spent in an ancestor block
|
||||
return Err.DOUBLE_SPEND, None
|
||||
removal_coin_records[unspent.name] = unspent
|
||||
@ -376,22 +383,22 @@ async def validate_block_body(
|
||||
if rem not in additions_since_fork:
|
||||
# Check for spending a coin that does not exist in this fork
|
||||
return Err.UNKNOWN_UNSPENT, None
|
||||
new_coin, confirmed_height = additions_since_fork[rem]
|
||||
new_coin, confirmed_height, confirmed_timestamp = additions_since_fork[rem]
|
||||
new_coin_record: CoinRecord = CoinRecord(
|
||||
new_coin,
|
||||
confirmed_height,
|
||||
uint32(0),
|
||||
False,
|
||||
(rem in coinbases_since_fork),
|
||||
block.foliage_transaction_block.timestamp,
|
||||
False,
|
||||
confirmed_timestamp,
|
||||
)
|
||||
removal_coin_records[new_coin_record.name] = new_coin_record
|
||||
|
||||
# This check applies to both coins created before fork (pulled from coin_store),
|
||||
# and coins created after fork (additions_since_fork)>
|
||||
# and coins created after fork (additions_since_fork)
|
||||
if rem in removals_since_fork:
|
||||
# This coin was spent in the fork
|
||||
return Err.DOUBLE_SPEND, None
|
||||
return Err.DOUBLE_SPEND_IN_FORK, None
|
||||
|
||||
removed = 0
|
||||
for unspent in removal_coin_records.values():
|
||||
@ -422,8 +429,8 @@ async def validate_block_body(
|
||||
if fees < assert_fee_sum:
|
||||
return Err.RESERVE_FEE_CONDITION_FAILED, None
|
||||
|
||||
# 18. Check that the assert fee amount < maximum coin amount
|
||||
if fees > constants.MAX_COIN_AMOUNT:
|
||||
# 18. Check that the fee amount + farmer reward < maximum coin amount
|
||||
if fees + calculate_base_farmer_reward(height) > constants.MAX_COIN_AMOUNT:
|
||||
return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM, None
|
||||
|
||||
# 19. Check that the computed fees are equal to the fees in the block header
|
||||
|
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import random
|
||||
from dataclasses import replace
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
@ -31,6 +32,8 @@ from chia.util.merkle_set import MerkleSet
|
||||
from chia.util.prev_transaction_block import get_prev_transaction_block
|
||||
from chia.util.recursive_replace import recursive_replace
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_foliage(
|
||||
constants: ConsensusConstants,
|
||||
|
@ -201,8 +201,10 @@ class Blockchain(BlockchainInterface):
|
||||
block_generator: Optional[BlockGenerator] = await self.get_block_generator(block)
|
||||
except ValueError:
|
||||
return ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None
|
||||
assert block_generator is not None
|
||||
npc_result = get_name_puzzle_conditions(block_generator, self.constants.MAX_BLOCK_COST_CLVM, False)
|
||||
assert block_generator is not None and block.transactions_info is not None
|
||||
npc_result = get_name_puzzle_conditions(
|
||||
block_generator, min(self.constants.MAX_BLOCK_COST_CLVM, block.transactions_info.cost), False
|
||||
)
|
||||
removals, additions = block_removals_and_additions(block, npc_result.npc_list)
|
||||
else:
|
||||
removals, additions = [], list(block.get_included_reward_coins())
|
||||
|
@ -79,9 +79,6 @@ class CoinStore:
|
||||
)
|
||||
await self._add_coin_record(record)
|
||||
|
||||
for coin_name in removals:
|
||||
await self._set_spent(coin_name, block.height)
|
||||
|
||||
included_reward_coins = block.get_included_reward_coins()
|
||||
if block.height == 0:
|
||||
assert len(included_reward_coins) == 0
|
||||
@ -99,6 +96,9 @@ class CoinStore:
|
||||
)
|
||||
await self._add_coin_record(reward_coin_r)
|
||||
|
||||
for coin_name in removals:
|
||||
await self._set_spent(coin_name, block.height)
|
||||
|
||||
# Checks DB and DiffStores for CoinRecord with coin_name and returns it
|
||||
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]:
|
||||
if coin_name.hex() in self.coin_record_cache:
|
||||
@ -215,7 +215,7 @@ class CoinStore:
|
||||
async def _set_spent(self, coin_name: bytes32, index: uint32):
|
||||
current: Optional[CoinRecord] = await self.get_coin_record(coin_name)
|
||||
if current is None:
|
||||
return
|
||||
raise ValueError(f"Cannot spend a coin that does not exist in db: {coin_name}")
|
||||
spent: CoinRecord = CoinRecord(
|
||||
current.coin,
|
||||
current.confirmed_block_index,
|
||||
|
@ -19,7 +19,7 @@ class CoinRecord(Streamable):
|
||||
spent_block_index: uint32
|
||||
spent: bool
|
||||
coinbase: bool
|
||||
timestamp: uint64
|
||||
timestamp: uint64 # Timestamp of the block at height confirmed_block_index
|
||||
|
||||
@property
|
||||
def name(self) -> bytes32:
|
||||
|
@ -146,6 +146,7 @@ class Err(Enum):
|
||||
INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT = 119
|
||||
FUTURE_GENERATOR_REFS = 120 # All refs must be to blocks in the past
|
||||
GENERATOR_REF_HAS_NO_GENERATOR = 121
|
||||
DOUBLE_SPEND_IN_FORK = 122
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
|
@ -137,18 +137,15 @@ class WalletTool:
|
||||
self,
|
||||
amount: uint64,
|
||||
new_puzzle_hash: bytes32,
|
||||
coin: Coin,
|
||||
coins: List[Coin],
|
||||
condition_dic: Dict[ConditionOpcode, List[ConditionWithArgs]],
|
||||
fee: int = 0,
|
||||
secret_key: Optional[PrivateKey] = None,
|
||||
) -> List[CoinSolution]:
|
||||
spends = []
|
||||
spend_value = coin.amount
|
||||
puzzle_hash = coin.puzzle_hash
|
||||
if secret_key is None:
|
||||
secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash)
|
||||
pubkey = secret_key.get_g1()
|
||||
puzzle = puzzle_for_pk(bytes(pubkey))
|
||||
|
||||
spend_value = sum([c.amount for c in coins])
|
||||
|
||||
if ConditionOpcode.CREATE_COIN not in condition_dic:
|
||||
condition_dic[ConditionOpcode.CREATE_COIN] = []
|
||||
|
||||
@ -160,11 +157,20 @@ class WalletTool:
|
||||
change_puzzle_hash = self.get_new_puzzlehash()
|
||||
change_output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [change_puzzle_hash, int_to_bytes(change)])
|
||||
condition_dic[output.opcode].append(change_output)
|
||||
solution = self.make_solution(condition_dic)
|
||||
main_solution: Program = self.make_solution(condition_dic)
|
||||
else:
|
||||
solution = self.make_solution(condition_dic)
|
||||
main_solution = self.make_solution(condition_dic)
|
||||
|
||||
spends.append(CoinSolution(coin, puzzle, solution))
|
||||
for n, coin in enumerate(coins):
|
||||
puzzle_hash = coin.puzzle_hash
|
||||
if secret_key is None:
|
||||
secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash)
|
||||
pubkey = secret_key.get_g1()
|
||||
puzzle = puzzle_for_pk(bytes(pubkey))
|
||||
if n == 0:
|
||||
spends.append(CoinSolution(coin, puzzle, main_solution))
|
||||
else:
|
||||
spends.append(CoinSolution(coin, puzzle, self.make_solution({})))
|
||||
return spends
|
||||
|
||||
def sign_transaction(self, coin_solutions: List[CoinSolution]) -> SpendBundle:
|
||||
@ -200,6 +206,20 @@ class WalletTool:
|
||||
) -> SpendBundle:
|
||||
if condition_dic is None:
|
||||
condition_dic = {}
|
||||
transaction = self.generate_unsigned_transaction(amount, new_puzzle_hash, coin, condition_dic, fee)
|
||||
transaction = self.generate_unsigned_transaction(amount, new_puzzle_hash, [coin], condition_dic, fee)
|
||||
assert transaction is not None
|
||||
return self.sign_transaction(transaction)
|
||||
|
||||
def generate_signed_transaction_multiple_coins(
|
||||
self,
|
||||
amount: uint64,
|
||||
new_puzzle_hash: bytes32,
|
||||
coins: List[Coin],
|
||||
condition_dic: Dict[ConditionOpcode, List[ConditionWithArgs]] = None,
|
||||
fee: int = 0,
|
||||
) -> SpendBundle:
|
||||
if condition_dic is None:
|
||||
condition_dic = {}
|
||||
transaction = self.generate_unsigned_transaction(amount, new_puzzle_hash, coins, condition_dic, fee)
|
||||
assert transaction is not None
|
||||
return self.sign_transaction(transaction)
|
||||
|
@ -319,6 +319,12 @@ class Wallet:
|
||||
spends: List[CoinSolution] = []
|
||||
output_created = False
|
||||
|
||||
# Check for duplicates
|
||||
if primaries is not None:
|
||||
all_primaries_list = [(p["puzzlehash"], p["amount"]) for p in primaries] + [(newpuzzlehash, amount)]
|
||||
if len(set(all_primaries_list)) != len(all_primaries_list):
|
||||
raise ValueError("Cannot create two identical coins")
|
||||
|
||||
for coin in coins:
|
||||
self.log.info(f"coin from coins {coin}")
|
||||
puzzle: Program = await self.puzzle_for_puzzle_hash(coin.puzzle_hash)
|
||||
|
@ -5,21 +5,25 @@ import multiprocessing
|
||||
import time
|
||||
from dataclasses import replace
|
||||
from secrets import token_bytes
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from blspy import AugSchemeMPL, G2Element
|
||||
from clvm.casts import int_to_bytes
|
||||
|
||||
from chia.consensus.block_rewards import calculate_base_farmer_reward
|
||||
from chia.consensus.blockchain import ReceiveBlockResult
|
||||
from chia.consensus.coinbase import create_farmer_coin
|
||||
from chia.consensus.pot_iterations import is_overflow_block
|
||||
from chia.full_node.bundle_tools import detect_potential_template_generator
|
||||
from chia.types.blockchain_format.classgroup import ClassgroupElement
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.foliage import TransactionsInfo, FoliageTransactionBlock
|
||||
from chia.types.blockchain_format.foliage import TransactionsInfo
|
||||
from chia.types.blockchain_format.program import SerializedProgram
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.blockchain_format.slots import InfusedChallengeChainSubSlot
|
||||
from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
|
||||
from chia.types.condition_opcodes import ConditionOpcode
|
||||
from chia.types.condition_with_args import ConditionWithArgs
|
||||
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from chia.types.full_block import FullBlock
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
@ -28,6 +32,7 @@ from chia.util.block_tools import BlockTools, get_vdf_info_and_proof
|
||||
from chia.util.errors import Err
|
||||
from chia.util.hash import std_hash
|
||||
from chia.util.ints import uint8, uint64, uint32
|
||||
from chia.util.merkle_set import MerkleSet
|
||||
from chia.util.recursive_replace import recursive_replace
|
||||
from chia.util.wallet_tools import WalletTool
|
||||
from tests.core.fixtures import default_400_blocks # noqa: F401; noqa: F401
|
||||
@ -1543,6 +1548,54 @@ class TestBlockHeaderValidation:
|
||||
assert (await empty_blockchain.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
|
||||
class TestPreValidation:
|
||||
@pytest.mark.asyncio
|
||||
async def test_pre_validation_fails_bad_blocks(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(2)
|
||||
assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
block_bad = recursive_replace(
|
||||
blocks[-1], "reward_chain_block.total_iters", blocks[-1].reward_chain_block.total_iters + 1
|
||||
)
|
||||
res = await empty_blockchain.pre_validate_blocks_multiprocessing([blocks[0], block_bad], {})
|
||||
assert res[0].error is None
|
||||
assert res[1].error is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pre_validation(self, empty_blockchain, default_1000_blocks):
|
||||
blocks = default_1000_blocks[:100]
|
||||
start = time.time()
|
||||
n_at_a_time = min(multiprocessing.cpu_count(), 32)
|
||||
times_pv = []
|
||||
times_rb = []
|
||||
for i in range(0, len(blocks), n_at_a_time):
|
||||
end_i = min(i + n_at_a_time, len(blocks))
|
||||
blocks_to_validate = blocks[i:end_i]
|
||||
start_pv = time.time()
|
||||
res = await empty_blockchain.pre_validate_blocks_multiprocessing(blocks_to_validate, {})
|
||||
end_pv = time.time()
|
||||
times_pv.append(end_pv - start_pv)
|
||||
assert res is not None
|
||||
for n in range(end_i - i):
|
||||
assert res[n] is not None
|
||||
assert res[n].error is None
|
||||
block = blocks_to_validate[n]
|
||||
start_rb = time.time()
|
||||
result, err, _ = await empty_blockchain.receive_block(block, res[n])
|
||||
end_rb = time.time()
|
||||
times_rb.append(end_rb - start_rb)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
log.info(
|
||||
f"Added block {block.height} total iters {block.total_iters} "
|
||||
f"new slot? {len(block.finished_sub_slots)}, time {end_rb - start_rb}"
|
||||
)
|
||||
end = time.time()
|
||||
log.info(f"Total time: {end - start} seconds")
|
||||
log.info(f"Average pv: {sum(times_pv)/(len(blocks)/n_at_a_time)}")
|
||||
log.info(f"Average rb: {sum(times_rb)/(len(blocks))}")
|
||||
|
||||
|
||||
class TestBodyValidation:
|
||||
@pytest.mark.asyncio
|
||||
async def test_not_tx_block_but_has_data(self, empty_blockchain):
|
||||
@ -1866,6 +1919,481 @@ class TestBodyValidation:
|
||||
assert err == Err.GENERATOR_REF_HAS_NO_GENERATOR or err == Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT
|
||||
assert (await b.pre_validate_blocks_multiprocessing([block_2], {})) is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cost_exceeds_max(self, empty_blockchain):
|
||||
# 7
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
condition_dict = {ConditionOpcode.CREATE_COIN: []}
|
||||
for i in range(7000):
|
||||
output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt.pool_ph, int_to_bytes(i)])
|
||||
condition_dict[ConditionOpcode.CREATE_COIN].append(output)
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0], condition_dic=condition_dict
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[1] == Err.BLOCK_COST_EXCEEDS_MAX
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_clvm_must_not_fail(self, empty_blockchain):
|
||||
# 8
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_cost_in_block(self, empty_blockchain):
|
||||
# 9
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
block: FullBlock = blocks[-1]
|
||||
|
||||
# zero
|
||||
block_2: FullBlock = recursive_replace(block, "transactions_info.cost", uint64(0))
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
|
||||
)
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.INVALID_BLOCK_COST
|
||||
|
||||
# too low
|
||||
block_2: FullBlock = recursive_replace(block, "transactions_info.cost", uint64(1))
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
|
||||
)
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.GENERATOR_RUNTIME_ERROR
|
||||
|
||||
# too high
|
||||
block_2: FullBlock = recursive_replace(block, "transactions_info.cost", uint64(1000000))
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
|
||||
)
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.INVALID_BLOCK_COST
|
||||
|
||||
err = (await b.receive_block(block))[1]
|
||||
assert err is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_max_coin_amount(self):
|
||||
# 10
|
||||
# TODO: fix, this is not reaching validation. Because we can't create a block with such amounts due to uint64
|
||||
# limit in Coin
|
||||
|
||||
new_test_constants = test_constants.replace(
|
||||
**{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
|
||||
)
|
||||
b, connection, db_path = await create_blockchain(new_test_constants)
|
||||
bt_2 = BlockTools(new_test_constants)
|
||||
bt_2.constants = bt_2.constants.replace(
|
||||
**{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
|
||||
)
|
||||
blocks = bt_2.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt_2.get_pool_wallet_tool()
|
||||
|
||||
condition_dict = {ConditionOpcode.CREATE_COIN: []}
|
||||
output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt_2.pool_ph, int_to_bytes(2 ** 64)])
|
||||
condition_dict[ConditionOpcode.CREATE_COIN].append(output)
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction_multiple_coins(
|
||||
10,
|
||||
wt.get_new_puzzlehash(),
|
||||
list(blocks[1].get_included_reward_coins()),
|
||||
condition_dic=condition_dict,
|
||||
)
|
||||
try:
|
||||
blocks = bt_2.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
assert False
|
||||
except Exception as e:
|
||||
pass
|
||||
await connection.close()
|
||||
b.shut_down()
|
||||
db_path.unlink()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_merkle_roots(self, empty_blockchain):
|
||||
# 11
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
block: FullBlock = blocks[-1]
|
||||
|
||||
merkle_set = MerkleSet()
|
||||
# additions
|
||||
block_2 = recursive_replace(block, "foliage_transaction_block.additions_root", merkle_set.get_root())
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.BAD_ADDITION_ROOT
|
||||
|
||||
# removals
|
||||
merkle_set.add_already_hashed(std_hash(b"1"))
|
||||
block_2 = recursive_replace(block, "foliage_transaction_block.removals_root", merkle_set.get_root())
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.BAD_REMOVAL_ROOT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_filter(self, empty_blockchain):
|
||||
# 12
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
block: FullBlock = blocks[-1]
|
||||
block_2 = recursive_replace(block, "foliage_transaction_block.filter_hash", std_hash(b"3"))
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.INVALID_TRANSACTIONS_FILTER_HASH
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_duplicate_outputs(self, empty_blockchain):
|
||||
# 13
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
condition_dict = {ConditionOpcode.CREATE_COIN: []}
|
||||
for i in range(2):
|
||||
output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt.pool_ph, int_to_bytes(1)])
|
||||
condition_dict[ConditionOpcode.CREATE_COIN].append(output)
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0], condition_dic=condition_dict
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[1] == Err.DUPLICATE_OUTPUT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_duplicate_removals(self, empty_blockchain):
|
||||
# 14
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
tx_2: SpendBundle = wt.generate_signed_transaction(
|
||||
11, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
agg = SpendBundle.aggregate([tx, tx_2])
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=agg
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[1] == Err.DOUBLE_SPEND
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_double_spent_in_coin_store(self, empty_blockchain):
|
||||
# 15
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
tx_2: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-2].get_included_reward_coins())[0]
|
||||
)
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx_2
|
||||
)
|
||||
|
||||
assert (await b.receive_block(blocks[-1]))[1] == Err.DOUBLE_SPEND
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_double_spent_in_reorg(self, empty_blockchain):
|
||||
# 15
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
new_coin: Coin = tx.additions()[0]
|
||||
tx_2: SpendBundle = wt.generate_signed_transaction(10, wt.get_new_puzzlehash(), new_coin)
|
||||
# This is fine because coin exists
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx_2
|
||||
)
|
||||
assert (await b.receive_block(blocks[-1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
blocks = bt.get_consecutive_blocks(5, block_list_input=blocks, guarantee_transaction_block=True)
|
||||
for block in blocks[-5:]:
|
||||
assert (await b.receive_block(block))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
blocks_reorg = bt.get_consecutive_blocks(2, block_list_input=blocks[:-7], guarantee_transaction_block=True)
|
||||
assert (await b.receive_block(blocks_reorg[-2]))[0] == ReceiveBlockResult.ADDED_AS_ORPHAN
|
||||
assert (await b.receive_block(blocks_reorg[-1]))[0] == ReceiveBlockResult.ADDED_AS_ORPHAN
|
||||
|
||||
# Coin does not exist in reorg
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_2
|
||||
)
|
||||
|
||||
assert (await b.receive_block(blocks_reorg[-1]))[1] == Err.UNKNOWN_UNSPENT
|
||||
|
||||
# Finally add the block to the fork (spending both in same bundle, this is ephemeral)
|
||||
agg = SpendBundle.aggregate([tx, tx_2])
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks_reorg[:-1], guarantee_transaction_block=True, transaction_data=agg
|
||||
)
|
||||
assert (await b.receive_block(blocks_reorg[-1]))[1] is None
|
||||
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_2
|
||||
)
|
||||
assert (await b.receive_block(blocks_reorg[-1]))[1] == Err.DOUBLE_SPEND_IN_FORK
|
||||
|
||||
rewards_ph = wt.get_new_puzzlehash()
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
10,
|
||||
block_list_input=blocks_reorg[:-1],
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=rewards_ph,
|
||||
)
|
||||
for block in blocks_reorg[-10:]:
|
||||
r, e, _ = await b.receive_block(block)
|
||||
assert e is None
|
||||
|
||||
# ephemeral coin is spent
|
||||
first_coin = await b.coin_store.get_coin_record(new_coin.name())
|
||||
assert first_coin is not None and first_coin.spent
|
||||
second_coin = await b.coin_store.get_coin_record(tx_2.additions()[0].name())
|
||||
assert second_coin is not None and not second_coin.spent
|
||||
|
||||
farmer_coin = create_farmer_coin(
|
||||
blocks_reorg[-1].height,
|
||||
rewards_ph,
|
||||
calculate_base_farmer_reward(blocks_reorg[-1].height),
|
||||
bt.constants.GENESIS_CHALLENGE,
|
||||
)
|
||||
tx_3: SpendBundle = wt.generate_signed_transaction(10, wt.get_new_puzzlehash(), farmer_coin)
|
||||
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_3
|
||||
)
|
||||
assert (await b.receive_block(blocks_reorg[-1]))[1] is None
|
||||
|
||||
farmer_coin = await b.coin_store.get_coin_record(farmer_coin.name())
|
||||
assert first_coin is not None and farmer_coin.spent
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_minting_coin(self, empty_blockchain):
|
||||
# 16 TODO
|
||||
# 17 is tested in mempool tests
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_max_coin_amount_fee(self):
|
||||
# 18 TODO: we can't create a block with such amounts due to uint64
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_fees_in_block(self, empty_blockchain):
|
||||
# 19
|
||||
b = empty_blockchain
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
guarantee_transaction_block=True,
|
||||
farmer_reward_puzzle_hash=bt.pool_ph,
|
||||
pool_reward_puzzle_hash=bt.pool_ph,
|
||||
)
|
||||
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
wt: WalletTool = bt.get_pool_wallet_tool()
|
||||
|
||||
tx: SpendBundle = wt.generate_signed_transaction(
|
||||
10, wt.get_new_puzzlehash(), list(blocks[-1].get_included_reward_coins())[0]
|
||||
)
|
||||
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
|
||||
)
|
||||
block: FullBlock = blocks[-1]
|
||||
|
||||
# wrong feees
|
||||
block_2: FullBlock = recursive_replace(block, "transactions_info.fees", uint64(1239))
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
|
||||
)
|
||||
block_2 = recursive_replace(
|
||||
block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
|
||||
)
|
||||
new_m = block_2.foliage.foliage_transaction_block_hash
|
||||
new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
|
||||
block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
|
||||
|
||||
err = (await b.receive_block(block_2))[1]
|
||||
assert err == Err.INVALID_BLOCK_FEE_AMOUNT
|
||||
|
||||
|
||||
class TestReorgs:
|
||||
@pytest.mark.asyncio
|
||||
@ -2026,51 +2554,3 @@ class TestReorgs:
|
||||
for block in blocks_fork:
|
||||
result, error_code, _ = await b.receive_block(block)
|
||||
assert error_code is None
|
||||
|
||||
|
||||
class TestPreValidation:
|
||||
@pytest.mark.asyncio
|
||||
async def test_pre_validation_fails_bad_blocks(self, empty_blockchain):
|
||||
blocks = bt.get_consecutive_blocks(2)
|
||||
assert (await empty_blockchain.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
|
||||
|
||||
block_bad = recursive_replace(
|
||||
blocks[-1], "reward_chain_block.total_iters", blocks[-1].reward_chain_block.total_iters + 1
|
||||
)
|
||||
res = await empty_blockchain.pre_validate_blocks_multiprocessing([blocks[0], block_bad], {})
|
||||
assert res[0].error is None
|
||||
assert res[1].error is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pre_validation(self, empty_blockchain, default_1000_blocks):
|
||||
blocks = default_1000_blocks[:100]
|
||||
start = time.time()
|
||||
n_at_a_time = min(multiprocessing.cpu_count(), 32)
|
||||
times_pv = []
|
||||
times_rb = []
|
||||
for i in range(0, len(blocks), n_at_a_time):
|
||||
end_i = min(i + n_at_a_time, len(blocks))
|
||||
blocks_to_validate = blocks[i:end_i]
|
||||
start_pv = time.time()
|
||||
res = await empty_blockchain.pre_validate_blocks_multiprocessing(blocks_to_validate, {})
|
||||
end_pv = time.time()
|
||||
times_pv.append(end_pv - start_pv)
|
||||
assert res is not None
|
||||
for n in range(end_i - i):
|
||||
assert res[n] is not None
|
||||
assert res[n].error is None
|
||||
block = blocks_to_validate[n]
|
||||
start_rb = time.time()
|
||||
result, err, _ = await empty_blockchain.receive_block(block, res[n])
|
||||
end_rb = time.time()
|
||||
times_rb.append(end_rb - start_rb)
|
||||
assert err is None
|
||||
assert result == ReceiveBlockResult.NEW_PEAK
|
||||
log.info(
|
||||
f"Added block {block.height} total iters {block.total_iters} "
|
||||
f"new slot? {len(block.finished_sub_slots)}, time {end_rb - start_rb}"
|
||||
)
|
||||
end = time.time()
|
||||
log.info(f"Total time: {end - start} seconds")
|
||||
log.info(f"Average pv: {sum(times_pv)/(len(blocks)/n_at_a_time)}")
|
||||
log.info(f"Average rb: {sum(times_rb)/(len(blocks))}")
|
||||
|
Loading…
Reference in New Issue
Block a user