mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-31 18:14:25 -05:00
Merge commit '99448387ada5dd492d06261ab823c59c2a55b231' into catchup/long_lived_datalayer_merkle_blob_from_main_99448387ada5dd492d06261ab823c59c2a55b231
This commit is contained in:
@@ -14,7 +14,7 @@ from _pytest.capture import CaptureFixture
|
||||
|
||||
import chia._tests
|
||||
from chia._tests.util.misc import Marks, datacases
|
||||
from chia.cmds.gh import Per, TestCMD, get_gh_token
|
||||
from chia.cmds.dev.gh import Per, TestCMD, get_gh_token
|
||||
|
||||
test_root = Path(chia._tests.__file__).parent
|
||||
|
||||
|
||||
@@ -802,7 +802,7 @@ class UnspentLineageInfoCase:
|
||||
),
|
||||
],
|
||||
parent_with_diff_amount=True,
|
||||
expected_success=True,
|
||||
expected_success=False,
|
||||
),
|
||||
UnspentLineageInfoCase(
|
||||
id="Unspent with parent that has same puzzlehash and amount but is also unspent",
|
||||
@@ -884,13 +884,11 @@ async def test_get_unspent_lineage_info_for_puzzle_hash(case: UnspentLineageInfo
|
||||
if case.expected_success:
|
||||
assert result == UnspentLineageInfo(
|
||||
coin_id=bytes32(TEST_COIN_ID),
|
||||
coin_amount=TEST_AMOUNT,
|
||||
parent_id=(
|
||||
bytes32(TEST_PARENT_ID_DIFFERENT_AMOUNT)
|
||||
if case.parent_with_diff_amount
|
||||
else bytes32(TEST_PARENT_ID)
|
||||
),
|
||||
parent_amount=TEST_PARENT_DIFFERENT_AMOUNT if case.parent_with_diff_amount else TEST_AMOUNT,
|
||||
parent_parent_id=bytes32(TEST_PARENT_PARENT_ID),
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -2957,9 +2957,8 @@ async def test_skip_error_items() -> None:
|
||||
called += 1
|
||||
raise RuntimeError("failed to find fast forward coin")
|
||||
|
||||
result = await mempool.create_block_generator(local_get_unspent_lineage_info, DEFAULT_CONSTANTS, uint32(10))
|
||||
assert result is not None
|
||||
generator, _, _, _ = result
|
||||
generator = await mempool.create_block_generator(local_get_unspent_lineage_info, DEFAULT_CONSTANTS, uint32(10))
|
||||
assert generator is not None
|
||||
|
||||
assert called == 3
|
||||
assert generator.program == SerializedProgram.from_bytes(bytes.fromhex("ff01ff8080"))
|
||||
@@ -3247,21 +3246,21 @@ async def test_create_block_generator() -> None:
|
||||
mempool.add_to_pool(mi)
|
||||
invariant_check_mempool(mempool)
|
||||
|
||||
block = await mempool.create_block_generator(get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0))
|
||||
assert block is not None
|
||||
generator, signature, additions, _ = block
|
||||
generator = await mempool.create_block_generator(
|
||||
get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0)
|
||||
)
|
||||
assert generator is not None
|
||||
|
||||
assert set(additions) == expected_additions
|
||||
|
||||
assert len(additions) == len(expected_additions)
|
||||
assert signature == expected_signature
|
||||
assert set(generator.additions) == expected_additions
|
||||
assert len(generator.additions) == len(expected_additions)
|
||||
assert generator.signature == expected_signature
|
||||
|
||||
err, conds = run_block_generator2(
|
||||
bytes(generator.program),
|
||||
generator.generator_refs,
|
||||
test_constants.MAX_BLOCK_COST_CLVM,
|
||||
0,
|
||||
signature,
|
||||
generator.signature,
|
||||
None,
|
||||
test_constants,
|
||||
)
|
||||
@@ -3278,7 +3277,7 @@ async def test_create_block_generator() -> None:
|
||||
assert Coin(spend.coin_id, add2[0], uint64(add2[1])) in expected_additions
|
||||
num_additions += 1
|
||||
|
||||
assert num_additions == len(additions)
|
||||
assert num_additions == len(generator.additions)
|
||||
invariant_check_mempool(mempool)
|
||||
|
||||
|
||||
@@ -3356,11 +3355,7 @@ async def test_lineage_cache(seeded_random: random.Random) -> None:
|
||||
called = 0
|
||||
|
||||
info1 = UnspentLineageInfo(
|
||||
bytes32.random(seeded_random),
|
||||
uint64.from_bytes(seeded_random.randbytes(8)),
|
||||
bytes32.random(seeded_random),
|
||||
uint64.from_bytes(seeded_random.randbytes(8)),
|
||||
bytes32.random(seeded_random),
|
||||
bytes32.random(seeded_random), bytes32.random(seeded_random), bytes32.random(seeded_random)
|
||||
)
|
||||
|
||||
async def callback1(ph: bytes32) -> Optional[UnspentLineageInfo]:
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import logging
|
||||
import random
|
||||
from collections.abc import Awaitable, Collection, Sequence
|
||||
from typing import Any, Callable, ClassVar, Optional, Union
|
||||
|
||||
@@ -9,14 +10,17 @@ import pytest
|
||||
from chia_rs import (
|
||||
ELIGIBLE_FOR_DEDUP,
|
||||
ELIGIBLE_FOR_FF,
|
||||
MEMPOOL_MODE,
|
||||
AugSchemeMPL,
|
||||
ConsensusConstants,
|
||||
G2Element,
|
||||
get_conditions_from_spendbundle,
|
||||
run_block_generator2,
|
||||
)
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint8, uint32, uint64
|
||||
from chiabip158 import PyBIP158
|
||||
from clvm.casts import int_to_bytes
|
||||
|
||||
from chia._tests.conftest import ConsensusMode
|
||||
from chia._tests.util.misc import invariant_check_mempool
|
||||
@@ -41,6 +45,7 @@ from chia.protocols.full_node_protocol import RequestBlock, RespondBlock
|
||||
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
||||
from chia.simulator.full_node_simulator import FullNodeSimulator
|
||||
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from chia.simulator.wallet_tools import WalletTool
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.program import INFINITE_COST, Program
|
||||
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
||||
@@ -48,6 +53,7 @@ from chia.types.clvm_cost import CLVMCost
|
||||
from chia.types.coin_record import CoinRecord
|
||||
from chia.types.coin_spend import CoinSpend, make_spend
|
||||
from chia.types.condition_opcodes import ConditionOpcode
|
||||
from chia.types.condition_with_args import ConditionWithArgs
|
||||
from chia.types.eligible_coin_spends import (
|
||||
DedupCoinSpend,
|
||||
EligibilityAndAdditions,
|
||||
@@ -63,6 +69,10 @@ from chia.types.spend_bundle import SpendBundle
|
||||
from chia.types.spend_bundle_conditions import SpendBundleConditions, SpendConditions
|
||||
from chia.util.errors import Err, ValidationError
|
||||
from chia.wallet.conditions import AssertCoinAnnouncement
|
||||
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
|
||||
DEFAULT_HIDDEN_PUZZLE_HASH,
|
||||
calculate_synthetic_secret_key,
|
||||
)
|
||||
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
|
||||
from chia.wallet.wallet import Wallet
|
||||
from chia.wallet.wallet_coin_record import WalletCoinRecord
|
||||
@@ -2202,9 +2212,7 @@ class TestCoins:
|
||||
self.coin_records[c.name()] = CoinRecord(c, uint32(0), uint32(0), False, TEST_TIMESTAMP)
|
||||
self.lineage_info = {}
|
||||
for ph, c in lineage.items():
|
||||
self.lineage_info[ph] = UnspentLineageInfo(
|
||||
c.name(), c.amount, c.parent_coin_info, uint64(1337), bytes32([42] * 32)
|
||||
)
|
||||
self.lineage_info[ph] = UnspentLineageInfo(c.name(), c.parent_coin_info, bytes32([42] * 32))
|
||||
|
||||
def spend_coin(self, coin_id: bytes32, height: uint32 = uint32(10)) -> None:
|
||||
self.coin_records[coin_id] = dataclasses.replace(self.coin_records[coin_id], spent_block_index=height)
|
||||
@@ -2215,9 +2223,7 @@ class TestCoins:
|
||||
else:
|
||||
assert coin.puzzle_hash == puzzle_hash
|
||||
prev = self.lineage_info[puzzle_hash]
|
||||
self.lineage_info[puzzle_hash] = UnspentLineageInfo(
|
||||
coin.name(), coin.amount, coin.parent_coin_info, prev.coin_amount, prev.coin_id
|
||||
)
|
||||
self.lineage_info[puzzle_hash] = UnspentLineageInfo(coin.name(), coin.parent_coin_info, prev.coin_id)
|
||||
|
||||
async def get_coin_records(self, coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
||||
ret = []
|
||||
@@ -2501,3 +2507,159 @@ async def test_advancing_ff(use_optimization: bool) -> None:
|
||||
spend = item.bundle_coin_spends[spend_a.coin.name()]
|
||||
assert spend.eligible_for_fast_forward
|
||||
assert spend.latest_singleton_coin == spend_c.coin.name()
|
||||
|
||||
|
||||
@pytest.fixture(name="test_wallet")
|
||||
def test_wallet_fixture() -> WalletTool:
|
||||
return WalletTool(DEFAULT_CONSTANTS)
|
||||
|
||||
|
||||
@pytest.fixture(name="transactions_1000")
|
||||
def transactions_1000_fixture(test_wallet: WalletTool, seeded_random: random.Random) -> list[SpendBundle]:
|
||||
op = ConditionOpcode
|
||||
bundles: list[SpendBundle] = []
|
||||
|
||||
test_conditions = [
|
||||
op.AGG_SIG_PARENT,
|
||||
op.AGG_SIG_PUZZLE,
|
||||
op.AGG_SIG_AMOUNT,
|
||||
op.AGG_SIG_PUZZLE_AMOUNT,
|
||||
op.AGG_SIG_PARENT_AMOUNT,
|
||||
op.AGG_SIG_PARENT_PUZZLE,
|
||||
op.AGG_SIG_UNSAFE,
|
||||
op.AGG_SIG_ME,
|
||||
op.CREATE_COIN,
|
||||
op.CREATE_COIN_ANNOUNCEMENT,
|
||||
op.CREATE_PUZZLE_ANNOUNCEMENT,
|
||||
op.ASSERT_MY_COIN_ID,
|
||||
op.ASSERT_MY_PARENT_ID,
|
||||
op.ASSERT_MY_PUZZLEHASH,
|
||||
op.ASSERT_MY_AMOUNT,
|
||||
]
|
||||
|
||||
print("generating 1000 coins and spend bundles")
|
||||
for i in range(1000):
|
||||
# generate a coin with a dummy parent coin ID
|
||||
puzzle = test_wallet.get_new_puzzle()
|
||||
coin = Coin(bytes32(i.to_bytes(32, byteorder="big")), puzzle.get_tree_hash(), uint64(100 * i))
|
||||
|
||||
conditions: dict[ConditionOpcode, list[ConditionWithArgs]] = {
|
||||
ConditionOpcode.CREATE_COIN: [
|
||||
ConditionWithArgs(
|
||||
ConditionOpcode.CREATE_COIN, [test_wallet.get_new_puzzle().get_tree_hash(), int_to_bytes(25 * i)]
|
||||
)
|
||||
]
|
||||
}
|
||||
# generate a somewhat arbitrarty set of conditions for the spend, just
|
||||
# to have some diversity and make the block interesting
|
||||
num_conditions = seeded_random.randint(0, 10)
|
||||
for c in seeded_random.sample(test_conditions, num_conditions):
|
||||
if c in set(
|
||||
[
|
||||
op.AGG_SIG_PARENT,
|
||||
op.AGG_SIG_PUZZLE,
|
||||
op.AGG_SIG_AMOUNT,
|
||||
op.AGG_SIG_PUZZLE_AMOUNT,
|
||||
op.AGG_SIG_PARENT_AMOUNT,
|
||||
op.AGG_SIG_PARENT_PUZZLE,
|
||||
op.AGG_SIG_UNSAFE,
|
||||
op.AGG_SIG_ME,
|
||||
]
|
||||
):
|
||||
secret_key = test_wallet.get_private_key_for_puzzle_hash(coin.puzzle_hash)
|
||||
synthetic_secret_key = calculate_synthetic_secret_key(secret_key, DEFAULT_HIDDEN_PUZZLE_HASH)
|
||||
cond = ConditionWithArgs(c, [bytes(synthetic_secret_key.get_g1()), b"foobar"])
|
||||
elif c == op.CREATE_COIN:
|
||||
cond = ConditionWithArgs(c, [test_wallet.get_new_puzzle().get_tree_hash(), int_to_bytes(i)])
|
||||
elif c in set([op.CREATE_COIN_ANNOUNCEMENT, op.CREATE_PUZZLE_ANNOUNCEMENT]):
|
||||
cond = ConditionWithArgs(c, [b"foobar"])
|
||||
elif c == op.ASSERT_MY_COIN_ID:
|
||||
cond = ConditionWithArgs(c, [coin.name()])
|
||||
elif c == op.ASSERT_MY_PARENT_ID:
|
||||
cond = ConditionWithArgs(c, [coin.parent_coin_info])
|
||||
elif c == op.ASSERT_MY_PUZZLEHASH:
|
||||
cond = ConditionWithArgs(c, [coin.puzzle_hash])
|
||||
elif c == op.ASSERT_MY_AMOUNT:
|
||||
cond = ConditionWithArgs(c, [int_to_bytes(coin.amount)])
|
||||
conditions.setdefault(c, []).append(cond)
|
||||
|
||||
# generate a spend of that coin
|
||||
bundle = test_wallet.generate_signed_transaction(
|
||||
uint64(50 * i), test_wallet.get_new_puzzle().get_tree_hash(), coin, conditions
|
||||
)
|
||||
bundles.append(bundle)
|
||||
return bundles
|
||||
|
||||
|
||||
# if we try to fill the mempool with more than 550, all spends won't
|
||||
# necessarily fit in the block, which the test assumes
|
||||
@pytest.mark.anyio
|
||||
@pytest.mark.parametrize("mempool_size", [1, 2, 50, 100, 300, 400, 550])
|
||||
@pytest.mark.parametrize("seed", [0, 1, 2, 3, 4, 5, 6])
|
||||
async def test_create_block_generator(mempool_size: int, seed: int, transactions_1000: list[SpendBundle]) -> None:
|
||||
bundles = transactions_1000
|
||||
all_coins = [s.coin for b in bundles for s in b.coin_spends]
|
||||
coins = TestCoins(all_coins, {})
|
||||
|
||||
rng = random.Random(seed)
|
||||
|
||||
# run the test multiple times, generating different combinations of mempools
|
||||
mempool_manager = await setup_mempool(coins)
|
||||
|
||||
included_bundles = rng.sample(bundles, mempool_size)
|
||||
expected_additions: set[Coin] = set()
|
||||
expected_removals: set[Coin] = set()
|
||||
expected_signature = G2Element()
|
||||
for sb in included_bundles:
|
||||
pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
|
||||
bundle_add_info = await mempool_manager.add_spend_bundle(
|
||||
sb, pre_validation, sb.name(), first_added_height=uint32(1)
|
||||
)
|
||||
expected_additions.update(sb.additions())
|
||||
expected_removals.update(sb.removals())
|
||||
|
||||
expected_signature += sb.aggregated_signature
|
||||
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
||||
item = mempool_manager.get_mempool_item(sb.name())
|
||||
assert item is not None
|
||||
all_items = mempool_manager.mempool.all_items()
|
||||
assert len(list(all_items)) == len(included_bundles)
|
||||
|
||||
invariant_check_mempool(mempool_manager.mempool)
|
||||
|
||||
assert mempool_manager.peak is not None
|
||||
new_block_gen = await mempool_manager.create_block_generator(mempool_manager.peak.header_hash)
|
||||
assert new_block_gen is not None
|
||||
|
||||
# now, make sure the generator we got is valid
|
||||
|
||||
assert len(expected_additions) == len(new_block_gen.additions)
|
||||
assert expected_additions == set(new_block_gen.additions)
|
||||
assert len(expected_removals) == len(new_block_gen.removals)
|
||||
assert expected_removals == set(new_block_gen.removals)
|
||||
assert expected_signature == new_block_gen.signature
|
||||
|
||||
err, conds = run_block_generator2(
|
||||
bytes(new_block_gen.program),
|
||||
new_block_gen.generator_refs,
|
||||
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
||||
MEMPOOL_MODE,
|
||||
new_block_gen.signature,
|
||||
None,
|
||||
DEFAULT_CONSTANTS,
|
||||
)
|
||||
|
||||
assert err is None
|
||||
assert conds is not None
|
||||
|
||||
assert len(conds.spends) == len(expected_removals)
|
||||
assert conds.cost < DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
|
||||
|
||||
num_additions = 0
|
||||
for spend in conds.spends:
|
||||
assert Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)) in expected_removals
|
||||
for add2 in spend.create_coin:
|
||||
assert Coin(spend.coin_id, add2[0], uint64(add2[1])) in expected_additions
|
||||
num_additions += 1
|
||||
|
||||
assert num_additions == len(new_block_gen.additions)
|
||||
|
||||
@@ -110,11 +110,7 @@ async def test_process_fast_forward_spends_latest_unspent() -> None:
|
||||
test_amount = uint64(3)
|
||||
test_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, test_amount)
|
||||
test_unspent_lineage_info = UnspentLineageInfo(
|
||||
coin_id=test_coin.name(),
|
||||
coin_amount=test_coin.amount,
|
||||
parent_id=test_coin.parent_coin_info,
|
||||
parent_amount=test_coin.amount,
|
||||
parent_parent_id=TEST_COIN_ID,
|
||||
coin_id=test_coin.name(), parent_id=test_coin.parent_coin_info, parent_parent_id=TEST_COIN_ID
|
||||
)
|
||||
|
||||
async def get_unspent_lineage_info_for_puzzle_hash(puzzle_hash: bytes32) -> Optional[UnspentLineageInfo]:
|
||||
@@ -141,11 +137,7 @@ async def test_process_fast_forward_spends_latest_unspent() -> None:
|
||||
child_coin = item.bundle_coin_spends[test_coin.name()].additions[0]
|
||||
expected_fast_forward_spends = {
|
||||
IDENTITY_PUZZLE_HASH: UnspentLineageInfo(
|
||||
coin_id=child_coin.name(),
|
||||
coin_amount=child_coin.amount,
|
||||
parent_id=test_coin.name(),
|
||||
parent_amount=test_coin.amount,
|
||||
parent_parent_id=test_coin.parent_coin_info,
|
||||
coin_id=child_coin.name(), parent_id=test_coin.name(), parent_parent_id=test_coin.parent_coin_info
|
||||
)
|
||||
}
|
||||
# We have set the next version from our additions to chain ff spends
|
||||
@@ -202,9 +194,7 @@ def test_perform_the_fast_forward() -> None:
|
||||
test_spend_data = BundleCoinSpend(test_coin_spend, False, True, [test_child_coin])
|
||||
test_unspent_lineage_info = UnspentLineageInfo(
|
||||
coin_id=latest_unspent_coin.name(),
|
||||
coin_amount=latest_unspent_coin.amount,
|
||||
parent_id=latest_unspent_coin.parent_coin_info,
|
||||
parent_amount=test_child_coin.amount,
|
||||
parent_parent_id=test_child_coin.parent_coin_info,
|
||||
)
|
||||
# Start from a fresh state of fast forward spends
|
||||
@@ -227,9 +217,7 @@ def test_perform_the_fast_forward() -> None:
|
||||
# (previously latest unspent)
|
||||
expected_unspent_lineage_info = UnspentLineageInfo(
|
||||
coin_id=expected_child_coin.name(),
|
||||
coin_amount=expected_child_coin.amount,
|
||||
parent_id=latest_unspent_coin.name(),
|
||||
parent_amount=latest_unspent_coin.amount,
|
||||
parent_parent_id=latest_unspent_coin.parent_coin_info,
|
||||
)
|
||||
assert fast_forward_spends == {test_ph: expected_unspent_lineage_info}
|
||||
@@ -301,7 +289,7 @@ def make_singleton_coin_spend(
|
||||
|
||||
|
||||
async def prepare_singleton_eve(
|
||||
sim: SpendSim, sim_client: SimClient, is_eligible_for_ff: bool, start_amount: uint64, singleton_amount: uint64
|
||||
sim: SpendSim, sim_client: SimClient, is_eligible_for_ff: bool, singleton_amount: uint64
|
||||
) -> tuple[Program, CoinSpend, Program]:
|
||||
# Generate starting info
|
||||
key_lookup = KeyTool()
|
||||
@@ -319,11 +307,11 @@ async def prepare_singleton_eve(
|
||||
starting_coin = records[0].coin
|
||||
# Launching
|
||||
conditions, launcher_coin_spend = singleton_top_layer.launch_conditions_and_coinsol(
|
||||
coin=starting_coin, inner_puzzle=inner_puzzle, comment=[], amount=start_amount
|
||||
coin=starting_coin, inner_puzzle=inner_puzzle, comment=[], amount=singleton_amount
|
||||
)
|
||||
# Keep a remaining coin with an even amount
|
||||
conditions.append(
|
||||
Program.to([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, starting_coin.amount - start_amount - 1])
|
||||
Program.to([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, starting_coin.amount - singleton_amount - 1])
|
||||
)
|
||||
# Create a solution for standard transaction
|
||||
delegated_puzzle = p2_conditions.puzzle_for_conditions(conditions)
|
||||
@@ -351,10 +339,10 @@ async def prepare_singleton_eve(
|
||||
|
||||
|
||||
async def prepare_and_test_singleton(
|
||||
sim: SpendSim, sim_client: SimClient, is_eligible_for_ff: bool, start_amount: uint64, singleton_amount: uint64
|
||||
sim: SpendSim, sim_client: SimClient, is_eligible_for_ff: bool, singleton_amount: uint64
|
||||
) -> tuple[Coin, CoinSpend, Program, Coin]:
|
||||
inner_puzzle, eve_coin_spend, eve_signing_puzzle = await prepare_singleton_eve(
|
||||
sim, sim_client, is_eligible_for_ff, start_amount, singleton_amount
|
||||
sim, sim_client, is_eligible_for_ff, singleton_amount
|
||||
)
|
||||
# At this point we don't have any unspent singleton
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
@@ -374,11 +362,7 @@ async def prepare_and_test_singleton(
|
||||
singleton_puzzle_hash
|
||||
)
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton.name(),
|
||||
coin_amount=singleton.amount,
|
||||
parent_id=eve_coin.name(),
|
||||
parent_amount=eve_coin.amount,
|
||||
parent_parent_id=eve_coin.parent_coin_info,
|
||||
coin_id=singleton.name(), parent_id=eve_coin.name(), parent_parent_id=eve_coin.parent_coin_info
|
||||
)
|
||||
return singleton, eve_coin_spend, inner_puzzle, remaining_coin
|
||||
|
||||
@@ -393,7 +377,7 @@ async def test_singleton_fast_forward_solo() -> None:
|
||||
SINGLETON_AMOUNT = uint64(1337)
|
||||
async with sim_and_client() as (sim, sim_client):
|
||||
singleton, eve_coin_spend, inner_puzzle, _ = await prepare_and_test_singleton(
|
||||
sim, sim_client, True, SINGLETON_AMOUNT, SINGLETON_AMOUNT
|
||||
sim, sim_client, True, SINGLETON_AMOUNT
|
||||
)
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
inner_puzzle_hash = inner_puzzle.get_tree_hash()
|
||||
@@ -410,9 +394,7 @@ async def test_singleton_fast_forward_solo() -> None:
|
||||
assert singleton_child.amount == SINGLETON_AMOUNT
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_child.name(),
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=eve_coin_spend.coin.name(),
|
||||
parent_amount=singleton.amount,
|
||||
parent_parent_id=eve_coin_spend.coin.parent_coin_info,
|
||||
)
|
||||
|
||||
@@ -434,12 +416,10 @@ async def test_singleton_fast_forward_different_block(is_eligible_for_ff: bool)
|
||||
get properly fast forwarded to the latest unspent (when it's eligible) or
|
||||
get correctly rejected as a double spend (when it's not eligible)
|
||||
"""
|
||||
START_AMOUNT = uint64(1337)
|
||||
# We're decrementing the next iteration's amount for testing purposes
|
||||
SINGLETON_AMOUNT = uint64(1335)
|
||||
SINGLETON_AMOUNT = uint64(1337)
|
||||
async with sim_and_client() as (sim, sim_client):
|
||||
singleton, eve_coin_spend, inner_puzzle, remaining_coin = await prepare_and_test_singleton(
|
||||
sim, sim_client, is_eligible_for_ff, START_AMOUNT, SINGLETON_AMOUNT
|
||||
sim, sim_client, is_eligible_for_ff, SINGLETON_AMOUNT
|
||||
)
|
||||
# Let's spend this first version, to create a bigger singleton child
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
@@ -475,11 +455,7 @@ async def test_singleton_fast_forward_different_block(is_eligible_for_ff: bool)
|
||||
singleton_child, [remaining_coin] = await get_singleton_and_remaining_coins(sim)
|
||||
assert singleton_child.amount == SINGLETON_AMOUNT
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_child.name(),
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=singleton.name(),
|
||||
parent_amount=singleton.amount,
|
||||
parent_parent_id=eve_coin_spend.coin.name(),
|
||||
coin_id=singleton_child.name(), parent_id=singleton.name(), parent_parent_id=eve_coin_spend.coin.name()
|
||||
)
|
||||
# Now let's spend the first version again (despite being already spent by now)
|
||||
remaining_spend_solution = SerializedProgram.from_program(
|
||||
@@ -506,11 +482,7 @@ async def test_singleton_fast_forward_different_block(is_eligible_for_ff: bool)
|
||||
)
|
||||
singleton_grandchild, [remaining_coin] = await get_singleton_and_remaining_coins(sim)
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_grandchild.name(),
|
||||
coin_amount=singleton_grandchild.amount,
|
||||
parent_id=singleton_child.name(),
|
||||
parent_amount=singleton_child.amount,
|
||||
parent_parent_id=singleton.name(),
|
||||
coin_id=singleton_grandchild.name(), parent_id=singleton_child.name(), parent_parent_id=singleton.name()
|
||||
)
|
||||
else:
|
||||
# As this singleton is not eligible for fast forward, attempting to
|
||||
@@ -526,12 +498,10 @@ async def test_singleton_fast_forward_same_block() -> None:
|
||||
singleton version, all in the same block, to make sure they get properly
|
||||
fast forwarded and chained down to a latest unspent version
|
||||
"""
|
||||
START_AMOUNT = uint64(1337)
|
||||
# We're decrementing the next iteration's amount for testing purposes
|
||||
SINGLETON_AMOUNT = uint64(1335)
|
||||
SINGLETON_AMOUNT = uint64(1337)
|
||||
async with sim_and_client() as (sim, sim_client):
|
||||
singleton, eve_coin_spend, inner_puzzle, remaining_coin = await prepare_and_test_singleton(
|
||||
sim, sim_client, True, START_AMOUNT, SINGLETON_AMOUNT
|
||||
sim, sim_client, True, SINGLETON_AMOUNT
|
||||
)
|
||||
# Let's spend this first version, to create a bigger singleton child
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
@@ -558,11 +528,7 @@ async def test_singleton_fast_forward_same_block() -> None:
|
||||
singleton_child, [remaining_coin] = await get_singleton_and_remaining_coins(sim)
|
||||
assert singleton_child.amount == SINGLETON_AMOUNT
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_child.name(),
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=singleton.name(),
|
||||
parent_amount=singleton.amount,
|
||||
parent_parent_id=eve_coin_spend.coin.name(),
|
||||
coin_id=singleton_child.name(), parent_id=singleton.name(), parent_parent_id=eve_coin_spend.coin.name()
|
||||
)
|
||||
# Now let's send 3 arbitrary spends of the already spent singleton in
|
||||
# one block. They should all properly fast forward
|
||||
@@ -596,14 +562,8 @@ async def test_singleton_fast_forward_same_block() -> None:
|
||||
assert unspent_lineage_info is not None
|
||||
# The unspent coin ID should reflect the latest version
|
||||
assert unspent_lineage_info.coin_id == latest_singleton.name()
|
||||
# The latest version should have the last random amount
|
||||
assert latest_singleton.amount == SINGLETON_AMOUNT
|
||||
# The unspent coin amount should reflect the latest version
|
||||
assert unspent_lineage_info.coin_amount == latest_singleton.amount
|
||||
# The unspent parent ID should reflect the latest version's parent
|
||||
assert unspent_lineage_info.parent_id == latest_singleton.parent_coin_info
|
||||
# The one before it should have the second last random amount
|
||||
assert unspent_lineage_info.parent_amount == SINGLETON_AMOUNT
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -615,7 +575,7 @@ async def test_mempool_items_immutability_on_ff() -> None:
|
||||
SINGLETON_AMOUNT = uint64(1337)
|
||||
async with sim_and_client() as (sim, sim_client):
|
||||
singleton, eve_coin_spend, inner_puzzle, remaining_coin = await prepare_and_test_singleton(
|
||||
sim, sim_client, True, SINGLETON_AMOUNT, SINGLETON_AMOUNT
|
||||
sim, sim_client, True, SINGLETON_AMOUNT
|
||||
)
|
||||
singleton_name = singleton.name()
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
@@ -649,11 +609,7 @@ async def test_mempool_items_immutability_on_ff() -> None:
|
||||
singleton_child_name = singleton_child.name()
|
||||
assert singleton_child.amount == SINGLETON_AMOUNT
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_child_name,
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=singleton_name,
|
||||
parent_amount=singleton.amount,
|
||||
parent_parent_id=eve_coin_spend.coin.name(),
|
||||
coin_id=singleton_child_name, parent_id=singleton_name, parent_parent_id=eve_coin_spend.coin.name()
|
||||
)
|
||||
# Now let's spend the first version again (despite being already spent
|
||||
# by now) to exercise its fast forward.
|
||||
@@ -694,11 +650,11 @@ async def test_double_spend_ff_spend_no_latest_unspent() -> None:
|
||||
This test covers the scenario where we receive a spend bundle with a
|
||||
singleton fast forward spend that has currently no unspent coin.
|
||||
"""
|
||||
test_amount = uint64(1337)
|
||||
singleton_amount = uint64(1337)
|
||||
async with sim_and_client() as (sim, sim_client):
|
||||
# Prepare a singleton spend
|
||||
singleton, eve_coin_spend, inner_puzzle, _ = await prepare_and_test_singleton(
|
||||
sim, sim_client, True, start_amount=test_amount, singleton_amount=test_amount
|
||||
sim, sim_client, True, singleton_amount=singleton_amount
|
||||
)
|
||||
singleton_name = singleton.name()
|
||||
singleton_puzzle_hash = eve_coin_spend.coin.puzzle_hash
|
||||
@@ -708,7 +664,7 @@ async def test_double_spend_ff_spend_no_latest_unspent() -> None:
|
||||
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
||||
inner_conditions: list[list[Any]] = [
|
||||
[ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"],
|
||||
[ConditionOpcode.CREATE_COIN, inner_puzzle_hash, test_amount],
|
||||
[ConditionOpcode.CREATE_COIN, inner_puzzle_hash, singleton_amount],
|
||||
]
|
||||
singleton_coin_spend, _ = make_singleton_coin_spend(eve_coin_spend, singleton, inner_puzzle, inner_conditions)
|
||||
# Get its current latest unspent info
|
||||
@@ -717,9 +673,7 @@ async def test_double_spend_ff_spend_no_latest_unspent() -> None:
|
||||
)
|
||||
assert unspent_lineage_info == UnspentLineageInfo(
|
||||
coin_id=singleton_name,
|
||||
coin_amount=test_amount,
|
||||
parent_id=eve_coin_spend.coin.name(),
|
||||
parent_amount=eve_coin_spend.coin.amount,
|
||||
parent_parent_id=eve_coin_spend.coin.parent_coin_info,
|
||||
)
|
||||
# Let's remove this latest unspent coin from the coin store
|
||||
|
||||
@@ -27,41 +27,72 @@ from chia.types.unfinished_block import UnfinishedBlock
|
||||
class TestNewPeak:
|
||||
@pytest.mark.anyio
|
||||
async def test_timelord_new_peak_basic(
|
||||
self, bt: BlockTools, timelord: tuple[TimelordAPI, ChiaServer], default_1000_blocks: list[FullBlock]
|
||||
self,
|
||||
timelord: tuple[TimelordAPI, ChiaServer],
|
||||
default_1000_blocks: list[FullBlock],
|
||||
one_node: tuple[list[FullNodeService], list[FullNodeSimulator], BlockTools],
|
||||
) -> None:
|
||||
[full_node_service], _, bt = one_node
|
||||
full_node = full_node_service._node
|
||||
async with create_blockchain(bt.constants, 2) as (b1, _):
|
||||
async with create_blockchain(bt.constants, 2) as (b2, _):
|
||||
timelord_api, _ = timelord
|
||||
for block in default_1000_blocks:
|
||||
await _validate_and_add_block(b1, block)
|
||||
await _validate_and_add_block(b2, block)
|
||||
await full_node.add_block(block)
|
||||
|
||||
peak = timelord_peak_from_block(b1, default_1000_blocks[-1])
|
||||
assert peak is not None
|
||||
assert timelord_api.timelord.new_peak is None
|
||||
await timelord_api.new_peak_timelord(peak)
|
||||
assert timelord_api.timelord.new_peak is not None
|
||||
assert timelord_api.timelord.new_peak.reward_chain_block.height == peak.reward_chain_block.height
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== peak.reward_chain_block.get_hash()
|
||||
)
|
||||
blocks = bt.get_consecutive_blocks(1, default_1000_blocks)
|
||||
await _validate_and_add_block(b1, blocks[-1])
|
||||
await _validate_and_add_block(b2, blocks[-1])
|
||||
|
||||
await timelord_api.new_peak_timelord(timelord_peak_from_block(b1, blocks[-1]))
|
||||
assert timelord_api.timelord.new_peak.reward_chain_block.height == blocks[-1].height
|
||||
await full_node.add_block(blocks[-1])
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== blocks[-1].reward_chain_block.get_hash()
|
||||
)
|
||||
fn_peak = full_node.blockchain.get_peak()
|
||||
assert fn_peak is not None and fn_peak.header_hash == blocks[-1].header_hash
|
||||
|
||||
blocks_1 = bt.get_consecutive_blocks(2, blocks)
|
||||
await _validate_and_add_block(b1, blocks_1[-2])
|
||||
await _validate_and_add_block(b1, blocks_1[-1])
|
||||
await timelord_api.new_peak_timelord(timelord_peak_from_block(b1, blocks_1[-2]))
|
||||
await full_node.add_block(blocks_1[-2])
|
||||
await timelord_api.new_peak_timelord(timelord_peak_from_block(b1, blocks_1[-1]))
|
||||
assert timelord_api.timelord.new_peak.reward_chain_block.height == blocks_1[-1].height
|
||||
await full_node.add_block(blocks_1[-1])
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== blocks_1[-1].reward_chain_block.get_hash()
|
||||
)
|
||||
fn_peak = full_node.blockchain.get_peak()
|
||||
assert fn_peak is not None and fn_peak.header_hash == blocks_1[-1].header_hash
|
||||
|
||||
# new unknown peak, weight less then curr peak
|
||||
blocks_2 = bt.get_consecutive_blocks(1, blocks)
|
||||
await _validate_and_add_block(b2, blocks_2[-1])
|
||||
await timelord_api.new_peak_timelord(timelord_peak_from_block(b2, blocks_2[-1]))
|
||||
await full_node.add_block(blocks_2[-1])
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.last_weight == blocks_1[-1].weight
|
||||
assert timelord_api.timelord.last_state.total_iters == blocks_1[-1].reward_chain_block.total_iters
|
||||
fn_peak = full_node.blockchain.get_peak()
|
||||
assert fn_peak is not None and fn_peak.header_hash == blocks_1[-1].header_hash
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_timelord_new_peak_unfinished_not_orphaned(
|
||||
@@ -106,7 +137,7 @@ class TestNewPeak:
|
||||
assert timelord_unf_block.reward_chain_block.total_iters <= new_peak.reward_chain_block.total_iters
|
||||
await timelord_api.new_peak_timelord(new_peak)
|
||||
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
|
||||
assert (
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
@@ -135,8 +166,11 @@ class TestNewPeak:
|
||||
assert timelord_api.timelord.new_peak is None
|
||||
await timelord_api.new_peak_timelord(peak)
|
||||
assert timelord_api.timelord.new_peak is not None
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.new_peak.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== peak.reward_chain_block.get_hash()
|
||||
)
|
||||
|
||||
# make two new blocks on tip, block_2 has higher total iterations
|
||||
@@ -177,7 +211,7 @@ class TestNewPeak:
|
||||
# add block_2 peak and make sure we skip it and prefer to finish block_1
|
||||
assert timelord_unf_block.reward_chain_block.total_iters <= new_peak.reward_chain_block.total_iters
|
||||
await timelord_api.new_peak_timelord(new_peak)
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
|
||||
# check that peak did not change
|
||||
assert (
|
||||
@@ -238,9 +272,11 @@ class TestNewPeak:
|
||||
assert peak is not None
|
||||
assert timelord_api.timelord.new_peak is None
|
||||
await timelord_api.new_peak_timelord(peak)
|
||||
assert timelord_api.timelord.new_peak is not None
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.new_peak.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== peak.reward_chain_block.get_hash()
|
||||
)
|
||||
|
||||
# make two new blocks on tip
|
||||
@@ -277,7 +313,7 @@ class TestNewPeak:
|
||||
assert timelord_unf_block.reward_chain_block.total_iters <= new_peak.reward_chain_block.total_iters
|
||||
await timelord_api.new_peak_timelord(new_peak)
|
||||
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
|
||||
assert (
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
@@ -286,22 +322,30 @@ class TestNewPeak:
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_timelord_new_peak_unfinished_eos(
|
||||
self, bt: BlockTools, timelord: tuple[TimelordAPI, ChiaServer], default_1000_blocks: list[FullBlock]
|
||||
self,
|
||||
one_node: tuple[list[FullNodeService], list[FullNodeSimulator], BlockTools],
|
||||
timelord: tuple[TimelordAPI, ChiaServer],
|
||||
default_1000_blocks: list[FullBlock],
|
||||
) -> None:
|
||||
[full_node_service], _, bt = one_node
|
||||
full_node = full_node_service._node
|
||||
async with create_blockchain(bt.constants, 2) as (b1, _):
|
||||
async with create_blockchain(bt.constants, 2) as (b2, _):
|
||||
timelord_api, _ = timelord
|
||||
for block in default_1000_blocks:
|
||||
await _validate_and_add_block(b1, block)
|
||||
await _validate_and_add_block(b2, block)
|
||||
await full_node.add_block(block)
|
||||
|
||||
peak = timelord_peak_from_block(b1, default_1000_blocks[-1])
|
||||
assert peak is not None
|
||||
assert timelord_api.timelord.new_peak is None
|
||||
await timelord_api.new_peak_timelord(peak)
|
||||
assert timelord_api.timelord.new_peak is not None
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
assert timelord_api.timelord.last_state.peak is not None
|
||||
assert (
|
||||
timelord_api.timelord.new_peak.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
|
||||
timelord_api.timelord.last_state.peak.reward_chain_block.get_hash()
|
||||
== peak.reward_chain_block.get_hash()
|
||||
)
|
||||
|
||||
# make two new blocks on tip, block_2 is in a new slot
|
||||
@@ -315,6 +359,9 @@ class TestNewPeak:
|
||||
|
||||
await _validate_and_add_block(b1, block_1)
|
||||
await _validate_and_add_block(b2, block_2)
|
||||
await full_node.add_block(block_2)
|
||||
fn_peak = full_node.blockchain.get_peak()
|
||||
assert fn_peak is not None and fn_peak.header_hash == block_2.header_hash
|
||||
|
||||
block_record = b2.block_record(block_2.header_hash)
|
||||
sub_slot_iters, difficulty = get_next_sub_slot_iters_and_difficulty(
|
||||
@@ -340,7 +387,8 @@ class TestNewPeak:
|
||||
new_peak = timelord_peak_from_block(b1, block_1)
|
||||
assert timelord_unf_block.reward_chain_block.total_iters >= new_peak.reward_chain_block.total_iters
|
||||
await timelord_api.new_peak_timelord(new_peak)
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await full_node.add_block(block_1)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
|
||||
# make sure we switch to lower iteration peak
|
||||
assert (
|
||||
@@ -348,6 +396,9 @@ class TestNewPeak:
|
||||
== new_peak.reward_chain_block.get_hash()
|
||||
)
|
||||
|
||||
fn_peak = full_node.blockchain.get_peak()
|
||||
assert fn_peak is not None and fn_peak.header_hash == block_1.header_hash
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_timelord_new_peak_node_sync(
|
||||
self,
|
||||
@@ -373,7 +424,7 @@ class TestNewPeak:
|
||||
assert (
|
||||
timelord_api.timelord.new_peak.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
|
||||
)
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
# make two new blocks on tip, block_2 has higher total iterations
|
||||
block_1 = bt.get_consecutive_blocks(1, default_1000_blocks)[-1]
|
||||
block_2 = bt.get_consecutive_blocks(
|
||||
@@ -394,7 +445,7 @@ class TestNewPeak:
|
||||
assert timelord_api.timelord.new_peak is not None
|
||||
assert peak.header_hash == block_2.header_hash
|
||||
assert peak_tl.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
|
||||
await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
|
||||
await time_out_assert(60, tl_new_peak_is_none, True, timelord_api)
|
||||
|
||||
await full_node.add_block(block_1)
|
||||
await _validate_and_add_block(b1, block_1)
|
||||
@@ -491,5 +542,5 @@ def timelord_peak_from_block(
|
||||
)
|
||||
|
||||
|
||||
def peak_new_peak_is_none(timelord: TimelordAPI) -> bool:
|
||||
def tl_new_peak_is_none(timelord: TimelordAPI) -> bool:
|
||||
return timelord.timelord.new_peak is None
|
||||
|
||||
+6
-6
@@ -12,7 +12,7 @@ from chia.cmds.completion import completion
|
||||
from chia.cmds.configure import configure_cmd
|
||||
from chia.cmds.data import data_cmd
|
||||
from chia.cmds.db import db_cmd
|
||||
from chia.cmds.dev import dev_cmd
|
||||
from chia.cmds.dev.main import dev_cmd
|
||||
from chia.cmds.farm import farm_cmd
|
||||
from chia.cmds.init import init_cmd
|
||||
from chia.cmds.keys import keys_cmd
|
||||
@@ -58,19 +58,19 @@ CONTEXT_SETTINGS = {
|
||||
def cli(
|
||||
ctx: click.Context,
|
||||
root_path: str,
|
||||
keys_root_path: Optional[str] = None,
|
||||
keys_root_path: str,
|
||||
passphrase_file: Optional[TextIOWrapper] = None,
|
||||
) -> None:
|
||||
from pathlib import Path
|
||||
|
||||
context = ChiaCliContext.set_default(ctx=ctx)
|
||||
context.root_path = Path(root_path)
|
||||
context.keys_root_path = Path(keys_root_path)
|
||||
|
||||
# keys_root_path and passphrase_file will be None if the passphrase options have been
|
||||
set_keys_root_path(Path(keys_root_path))
|
||||
|
||||
# passphrase_file will be None if the passphrase options have been
|
||||
# scrubbed from the CLI options
|
||||
if keys_root_path is not None:
|
||||
set_keys_root_path(Path(keys_root_path))
|
||||
|
||||
if passphrase_file is not None:
|
||||
import sys
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from chia_rs.sized_bytes import bytes32
|
||||
from typing_extensions import dataclass_transform
|
||||
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
from chia.util.default_root import DEFAULT_KEYS_ROOT_PATH, DEFAULT_ROOT_PATH
|
||||
from chia.util.streamable import is_type_SpecificOptional
|
||||
|
||||
SyncCmd = Callable[..., None]
|
||||
@@ -68,6 +68,7 @@ class ChiaCliContext:
|
||||
context_dict_key: ClassVar[str] = "_chia_cli_context"
|
||||
|
||||
root_path: pathlib.Path = DEFAULT_ROOT_PATH
|
||||
keys_root_path: pathlib.Path = DEFAULT_KEYS_ROOT_PATH
|
||||
expected_prefix: Optional[str] = None
|
||||
rpc_port: Optional[int] = None
|
||||
keys_fingerprint: Optional[int] = None
|
||||
|
||||
@@ -2,10 +2,10 @@ from __future__ import annotations
|
||||
|
||||
import click
|
||||
|
||||
from chia.cmds.gh import gh_group
|
||||
from chia.cmds.installers import installers_group
|
||||
from chia.cmds.mempool import mempool_cmd
|
||||
from chia.cmds.sim import sim_cmd
|
||||
from chia.cmds.dev.gh import gh_group
|
||||
from chia.cmds.dev.installers import installers_group
|
||||
from chia.cmds.dev.mempool import mempool_cmd
|
||||
from chia.cmds.dev.sim import sim_cmd
|
||||
|
||||
|
||||
@click.group("dev", help="Developer commands and tools")
|
||||
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
import click
|
||||
|
||||
from chia.cmds.cmd_classes import ChiaCliContext
|
||||
from chia.cmds.mempool_funcs import create_block_async, export_mempool_async, import_mempool_async
|
||||
from chia.cmds.dev.mempool_funcs import create_block_async, export_mempool_async, import_mempool_async
|
||||
|
||||
|
||||
@click.group("mempool", help="Debug the mempool")
|
||||
@@ -38,7 +38,7 @@ from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
||||
from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
|
||||
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from chia.types.full_block import FullBlock
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import BlockGenerator, NewBlockGenerator
|
||||
from chia.types.unfinished_block import UnfinishedBlock
|
||||
from chia.util.hash import std_hash
|
||||
from chia.util.prev_transaction_block import get_prev_transaction_block
|
||||
@@ -79,10 +79,7 @@ def compute_block_fee(additions: Sequence[Coin], removals: Sequence[Coin]) -> ui
|
||||
def create_foliage(
|
||||
constants: ConsensusConstants,
|
||||
reward_block_unfinished: RewardChainBlockUnfinished,
|
||||
block_generator: Optional[BlockGenerator],
|
||||
aggregate_sig: G2Element,
|
||||
additions: list[Coin],
|
||||
removals: list[Coin],
|
||||
new_block_gen: Optional[NewBlockGenerator],
|
||||
prev_block: Optional[BlockRecord],
|
||||
blocks: BlockRecordsProtocol,
|
||||
total_iters_sp: uint128,
|
||||
@@ -103,8 +100,7 @@ def create_foliage(
|
||||
Args:
|
||||
constants: consensus constants being used for this chain
|
||||
reward_block_unfinished: the reward block to look at, potentially at the signage point
|
||||
block_generator: transactions to add to the foliage block, if created
|
||||
aggregate_sig: aggregate of all transactions (or infinity element)
|
||||
new_block_gen: transactions to add to the foliage block, if created, including aggregate signature
|
||||
prev_block: the previous block at the signage point
|
||||
blocks: dict from header hash to blocks, of all ancestor blocks
|
||||
total_iters_sp: total iters at the signage point
|
||||
@@ -165,15 +161,16 @@ def create_foliage(
|
||||
foliage_transaction_block_hash: Optional[bytes32]
|
||||
|
||||
if is_transaction_block:
|
||||
cost = uint64(0)
|
||||
cost: uint64
|
||||
spend_bundle_fees: uint64
|
||||
|
||||
# Calculate the cost of transactions
|
||||
if block_generator is not None:
|
||||
cost = compute_cost(block_generator, constants, height)
|
||||
|
||||
spend_bundle_fees = compute_fees(additions, removals)
|
||||
if new_block_gen is not None:
|
||||
cost = compute_cost(new_block_gen, constants, height)
|
||||
spend_bundle_fees = compute_fees(new_block_gen.additions, new_block_gen.removals)
|
||||
else:
|
||||
spend_bundle_fees = uint64(0)
|
||||
cost = uint64(0)
|
||||
|
||||
reward_claims_incorporated = []
|
||||
if height > 0:
|
||||
@@ -215,14 +212,19 @@ def create_foliage(
|
||||
)
|
||||
reward_claims_incorporated += [pool_coin, farmer_coin]
|
||||
curr = blocks.block_record(curr.prev_hash)
|
||||
additions.extend(reward_claims_incorporated.copy())
|
||||
for coin in additions:
|
||||
|
||||
for coin in reward_claims_incorporated:
|
||||
tx_additions.append(coin)
|
||||
byte_array_tx.append(bytearray(coin.puzzle_hash))
|
||||
for coin in removals:
|
||||
cname = coin.name()
|
||||
tx_removals.append(cname)
|
||||
byte_array_tx.append(bytearray(cname))
|
||||
|
||||
if new_block_gen is not None:
|
||||
for coin in new_block_gen.additions:
|
||||
tx_additions.append(coin)
|
||||
byte_array_tx.append(bytearray(coin.puzzle_hash))
|
||||
for coin in new_block_gen.removals:
|
||||
cname = coin.name()
|
||||
tx_removals.append(cname)
|
||||
byte_array_tx.append(bytearray(cname))
|
||||
|
||||
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||
encoded = bytes(bip158.GetEncoded())
|
||||
@@ -247,8 +249,8 @@ def create_foliage(
|
||||
removals_root = bytes32(compute_merkle_set_root(tx_removals))
|
||||
|
||||
generator_hash = bytes32.zeros
|
||||
if block_generator is not None:
|
||||
generator_hash = std_hash(block_generator.program)
|
||||
if new_block_gen is not None:
|
||||
generator_hash = std_hash(new_block_gen.program)
|
||||
|
||||
generator_refs_hash = bytes32([1] * 32)
|
||||
filter_hash: bytes32 = std_hash(encoded)
|
||||
@@ -256,7 +258,7 @@ def create_foliage(
|
||||
transactions_info: Optional[TransactionsInfo] = TransactionsInfo(
|
||||
generator_hash,
|
||||
generator_refs_hash,
|
||||
aggregate_sig,
|
||||
new_block_gen.signature if new_block_gen else G2Element(),
|
||||
spend_bundle_fees,
|
||||
cost,
|
||||
reward_claims_incorporated,
|
||||
@@ -318,10 +320,7 @@ def create_unfinished_block(
|
||||
timestamp: uint64,
|
||||
blocks: BlockRecordsProtocol,
|
||||
seed: bytes = b"",
|
||||
block_generator: Optional[BlockGenerator] = None,
|
||||
aggregate_sig: G2Element = G2Element(),
|
||||
additions: Optional[list[Coin]] = None,
|
||||
removals: Optional[list[Coin]] = None,
|
||||
new_block_gen: Optional[NewBlockGenerator] = None,
|
||||
prev_block: Optional[BlockRecord] = None,
|
||||
finished_sub_slots_input: Optional[list[EndOfSubSlotBundle]] = None,
|
||||
compute_cost: Callable[[BlockGenerator, ConsensusConstants, uint32], uint64] = compute_block_cost,
|
||||
@@ -347,10 +346,7 @@ def create_unfinished_block(
|
||||
signage_point: signage point information (VDFs)
|
||||
timestamp: timestamp to add to the foliage block, if created
|
||||
seed: seed to randomize chain
|
||||
block_generator: transactions to add to the foliage block, if created
|
||||
aggregate_sig: aggregate of all transactions (or infinity element)
|
||||
additions: Coins added in spend_bundle
|
||||
removals: Coins removed in spend_bundle
|
||||
new_block_gen: transactions to add to the foliage block, if created, including aggregate signature
|
||||
prev_block: previous block (already in chain) from the signage point
|
||||
blocks: dictionary from header hash to SBR of all included SBR
|
||||
finished_sub_slots_input: finished_sub_slots at the signage point
|
||||
@@ -409,17 +405,10 @@ def create_unfinished_block(
|
||||
signage_point.rc_vdf,
|
||||
rc_sp_signature,
|
||||
)
|
||||
if additions is None:
|
||||
additions = []
|
||||
if removals is None:
|
||||
removals = []
|
||||
(foliage, foliage_transaction_block, transactions_info) = create_foliage(
|
||||
constants,
|
||||
rc_block,
|
||||
block_generator,
|
||||
aggregate_sig,
|
||||
additions,
|
||||
removals,
|
||||
new_block_gen,
|
||||
prev_block,
|
||||
blocks,
|
||||
total_iters_sp,
|
||||
@@ -440,8 +429,8 @@ def create_unfinished_block(
|
||||
foliage,
|
||||
foliage_transaction_block,
|
||||
transactions_info,
|
||||
block_generator.program if block_generator else None,
|
||||
[], # generator_refs
|
||||
new_block_gen.program if new_block_gen else None,
|
||||
new_block_gen.block_refs if new_block_gen else [],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import typing_extensions
|
||||
from aiosqlite import Cursor
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint32, uint64
|
||||
from clvm.casts import int_from_bytes
|
||||
|
||||
from chia.protocols.wallet_protocol import CoinState
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
@@ -613,27 +612,22 @@ class CoinStore:
|
||||
async with self.db_wrapper.reader_no_transaction() as conn:
|
||||
async with conn.execute(
|
||||
"SELECT unspent.coin_name, "
|
||||
"unspent.amount, "
|
||||
"unspent.coin_parent, "
|
||||
"parent.amount, "
|
||||
"parent.coin_parent "
|
||||
"FROM coin_record AS unspent INDEXED BY coin_puzzle_hash "
|
||||
"LEFT JOIN coin_record AS parent ON unspent.coin_parent = parent.coin_name "
|
||||
"WHERE unspent.spent_index = 0 "
|
||||
"AND parent.spent_index > 0 "
|
||||
"AND unspent.puzzle_hash = ? "
|
||||
"AND parent.puzzle_hash = unspent.puzzle_hash",
|
||||
"AND parent.puzzle_hash = unspent.puzzle_hash "
|
||||
"AND parent.amount = unspent.amount",
|
||||
(puzzle_hash,),
|
||||
) as cursor:
|
||||
rows = list(await cursor.fetchall())
|
||||
if len(rows) != 1:
|
||||
log.debug("Expected 1 unspent with puzzle hash %s, but found %s", puzzle_hash.hex(), len(rows))
|
||||
return None
|
||||
coin_id, coin_amount, parent_id, parent_amount, parent_parent_id = rows[0]
|
||||
coin_id, parent_id, parent_parent_id = rows[0]
|
||||
return UnspentLineageInfo(
|
||||
coin_id=bytes32(coin_id),
|
||||
coin_amount=uint64(int_from_bytes(coin_amount)),
|
||||
parent_id=bytes32(parent_id),
|
||||
parent_amount=uint64(int_from_bytes(parent_amount)),
|
||||
parent_parent_id=bytes32(parent_parent_id),
|
||||
coin_id=bytes32(coin_id), parent_id=bytes32(parent_id), parent_parent_id=bytes32(parent_parent_id)
|
||||
)
|
||||
|
||||
@@ -62,7 +62,7 @@ from chia.types.blockchain_format.sub_epoch_summary import SubEpochSummary
|
||||
from chia.types.coin_record import CoinRecord
|
||||
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from chia.types.full_block import FullBlock
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import BlockGenerator, NewBlockGenerator
|
||||
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
@@ -835,10 +835,7 @@ class FullNodeAPI:
|
||||
# 3. In a future sub-slot that we already know of
|
||||
|
||||
# Grab best transactions from Mempool for given tip target
|
||||
aggregate_signature: G2Element = G2Element()
|
||||
block_generator: Optional[BlockGenerator] = None
|
||||
additions: Optional[list[Coin]] = []
|
||||
removals: Optional[list[Coin]] = []
|
||||
new_block_gen: Optional[NewBlockGenerator]
|
||||
async with self.full_node.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high):
|
||||
peak: Optional[BlockRecord] = self.full_node.blockchain.get_peak()
|
||||
|
||||
@@ -863,12 +860,15 @@ class FullNodeAPI:
|
||||
while not curr_l_tb.is_transaction_block:
|
||||
curr_l_tb = self.full_node.blockchain.block_record(curr_l_tb.prev_hash)
|
||||
try:
|
||||
block = await self.full_node.mempool_manager.create_block_generator(curr_l_tb.header_hash)
|
||||
if block is not None:
|
||||
block_generator, aggregate_signature, additions, removals = block
|
||||
new_block_gen = await self.full_node.mempool_manager.create_block_generator(
|
||||
curr_l_tb.header_hash
|
||||
)
|
||||
except Exception as e:
|
||||
self.log.error(f"Traceback: {traceback.format_exc()}")
|
||||
self.full_node.log.error(f"Error making spend bundle {e} peak: {peak}")
|
||||
new_block_gen = None
|
||||
else:
|
||||
new_block_gen = None
|
||||
|
||||
def get_plot_sig(to_sign: bytes32, _extra: G1Element) -> G2Element:
|
||||
if to_sign == request.challenge_chain_sp:
|
||||
@@ -1005,10 +1005,7 @@ class FullNodeAPI:
|
||||
timestamp,
|
||||
self.full_node.blockchain,
|
||||
b"",
|
||||
block_generator,
|
||||
aggregate_signature,
|
||||
additions,
|
||||
removals,
|
||||
new_block_gen,
|
||||
prev_b,
|
||||
finished_sub_slots,
|
||||
)
|
||||
@@ -1065,9 +1062,6 @@ class FullNodeAPI:
|
||||
self.full_node.blockchain,
|
||||
b"",
|
||||
None,
|
||||
G2Element(),
|
||||
None,
|
||||
None,
|
||||
prev_b,
|
||||
finished_sub_slots,
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ from chia.types.blockchain_format.serialized_program import SerializedProgram
|
||||
from chia.types.clvm_cost import CLVMCost
|
||||
from chia.types.coin_spend import CoinSpend
|
||||
from chia.types.eligible_coin_spends import EligibleCoinSpends, SkipDedup, UnspentLineageInfo
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import NewBlockGenerator
|
||||
from chia.types.internal_mempool_item import InternalMempoolItem
|
||||
from chia.types.mempool_item import MempoolItem
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
@@ -492,7 +492,7 @@ class Mempool:
|
||||
get_unspent_lineage_info_for_puzzle_hash: Callable[[bytes32], Awaitable[Optional[UnspentLineageInfo]]],
|
||||
constants: ConsensusConstants,
|
||||
height: uint32,
|
||||
) -> Optional[tuple[BlockGenerator, G2Element, list[Coin], list[Coin]]]:
|
||||
) -> Optional[NewBlockGenerator]:
|
||||
"""
|
||||
height is needed in case we fast-forward a transaction and we need to
|
||||
re-run its puzzle.
|
||||
@@ -520,8 +520,10 @@ class Mempool:
|
||||
logging.INFO if duration < 1 else logging.WARNING,
|
||||
f"serializing block generator took {duration:0.2f} seconds",
|
||||
)
|
||||
return (
|
||||
BlockGenerator(SerializedProgram.from_bytes(block_program), []),
|
||||
return NewBlockGenerator(
|
||||
SerializedProgram.from_bytes(block_program),
|
||||
[],
|
||||
[],
|
||||
spend_bundle.aggregated_signature,
|
||||
additions,
|
||||
removals,
|
||||
|
||||
@@ -13,7 +13,6 @@ from chia_rs import (
|
||||
ELIGIBLE_FOR_FF,
|
||||
BLSCache,
|
||||
ConsensusConstants,
|
||||
G2Element,
|
||||
supports_fast_forward,
|
||||
validate_clvm_and_signature,
|
||||
)
|
||||
@@ -34,7 +33,7 @@ from chia.types.clvm_cost import CLVMCost
|
||||
from chia.types.coin_record import CoinRecord
|
||||
from chia.types.eligible_coin_spends import EligibilityAndAdditions, UnspentLineageInfo
|
||||
from chia.types.fee_rate import FeeRate
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import NewBlockGenerator
|
||||
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
||||
from chia.types.mempool_item import BundleCoinSpend, MempoolItem
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
@@ -244,7 +243,7 @@ class MempoolManager:
|
||||
async def create_block_generator(
|
||||
self,
|
||||
last_tb_header_hash: bytes32,
|
||||
) -> Optional[tuple[BlockGenerator, G2Element, list[Coin], list[Coin]]]:
|
||||
) -> Optional[NewBlockGenerator]:
|
||||
"""
|
||||
Returns a block generator program, the aggregate signature and all additions and removals, for a new block
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@ import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Optional, cast
|
||||
|
||||
from chia_rs import G2Element
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint32, uint64, uint128
|
||||
|
||||
@@ -25,7 +24,7 @@ from chia.types.blockchain_format.proof_of_space import calculate_prefix_bits
|
||||
from chia.types.coin_record import CoinRecord
|
||||
from chia.types.coin_spend import CoinSpend
|
||||
from chia.types.full_block import FullBlock
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import BlockGenerator, NewBlockGenerator
|
||||
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
from chia.types.spend_bundle_conditions import SpendBundleConditions
|
||||
@@ -840,8 +839,7 @@ class FullNodeRpcApi:
|
||||
return {"mempool_items": [item.to_json_dict() for item in items]}
|
||||
|
||||
async def create_block_generator(self, _: dict[str, Any]) -> EndpointResult:
|
||||
mempool_generator = BlockGenerator()
|
||||
signature = G2Element()
|
||||
gen = NewBlockGenerator()
|
||||
|
||||
# Grab best transactions from Mempool for given tip target
|
||||
async with self.service.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.low):
|
||||
@@ -849,9 +847,11 @@ class FullNodeRpcApi:
|
||||
|
||||
if peak is None:
|
||||
return {
|
||||
"generator": mempool_generator.program,
|
||||
"refs": mempool_generator.generator_refs,
|
||||
"sig": signature,
|
||||
"generator": gen.program,
|
||||
"refs": gen.block_refs,
|
||||
"additions": gen.additions,
|
||||
"removals": gen.removals,
|
||||
"sig": gen.signature,
|
||||
}
|
||||
|
||||
# Finds the last transaction block before this one
|
||||
@@ -863,14 +863,22 @@ class FullNodeRpcApi:
|
||||
start_time = time.monotonic()
|
||||
|
||||
try:
|
||||
result = await self.service.mempool_manager.create_block_generator(curr_l_tb.header_hash)
|
||||
assert result is not None
|
||||
mempool_generator, signature, _additions, _removals = result
|
||||
maybe_gen = await self.service.mempool_manager.create_block_generator(curr_l_tb.header_hash)
|
||||
if maybe_gen is None:
|
||||
self.service.log.error(f"failed to create block generator, peak: {peak}")
|
||||
else:
|
||||
gen = maybe_gen
|
||||
except Exception:
|
||||
self.service.log.exception(f"Error making spend bundle peak: {peak}")
|
||||
self.service.log.exception(f"Error creating block generator, peak: {peak}")
|
||||
self.service.log.info(f"Simulated block constructed in {time.monotonic() - start_time:0.2f} seconds")
|
||||
|
||||
return {"generator": mempool_generator.program, "refs": mempool_generator.generator_refs, "sig": signature}
|
||||
return {
|
||||
"generator": gen.program,
|
||||
"refs": gen.block_refs,
|
||||
"additions": gen.additions,
|
||||
"removals": gen.removals,
|
||||
"sig": gen.signature,
|
||||
}
|
||||
|
||||
def _get_spendbundle_type_cost(self, name: str) -> uint64:
|
||||
"""
|
||||
|
||||
@@ -97,7 +97,7 @@ from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
|
||||
from chia.types.condition_opcodes import ConditionOpcode
|
||||
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
|
||||
from chia.types.full_block import FullBlock
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.generator_types import BlockGenerator, NewBlockGenerator
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
from chia.types.unfinished_block import UnfinishedBlock
|
||||
from chia.util.bech32m import encode_puzzle_hash
|
||||
@@ -596,6 +596,8 @@ class BlockTools:
|
||||
skip_overflow: bool = False,
|
||||
min_signage_point: int = -1,
|
||||
) -> list[FullBlock]:
|
||||
# make a copy to not have different invocations affect each other
|
||||
block_refs = block_refs[:]
|
||||
assert num_blocks > 0
|
||||
if block_list_input is not None:
|
||||
block_list = block_list_input.copy()
|
||||
@@ -611,6 +613,9 @@ class BlockTools:
|
||||
tx_block_heights.append(b.height)
|
||||
|
||||
constants = self.constants
|
||||
|
||||
# this indicates whether the passed in transaction_data has been
|
||||
# included in a transaction block yet
|
||||
transaction_data_included = False
|
||||
if time_per_block is None:
|
||||
time_per_block = float(constants.SUB_SLOT_TIME_TARGET) / float(constants.SLOT_BLOCKS_TARGET)
|
||||
@@ -767,20 +772,9 @@ class BlockTools:
|
||||
continue
|
||||
|
||||
assert latest_block.header_hash in blocks
|
||||
additions = None
|
||||
removals = None
|
||||
if transaction_data_included:
|
||||
transaction_data = None
|
||||
block_refs = []
|
||||
if transaction_data is not None:
|
||||
additions = compute_additions_unchecked(transaction_data)
|
||||
removals = transaction_data.removals()
|
||||
elif include_transactions:
|
||||
assert wallet is not None
|
||||
assert rng is not None
|
||||
transaction_data, additions = make_spend_bundle(available_coins, wallet, rng)
|
||||
removals = transaction_data.removals()
|
||||
transaction_data_included = False
|
||||
|
||||
assert last_timestamp is not None
|
||||
if proof_of_space.pool_contract_puzzle_hash is not None:
|
||||
@@ -795,32 +789,48 @@ class BlockTools:
|
||||
else:
|
||||
pool_target = PoolTarget(self.pool_ph, uint32(0))
|
||||
|
||||
block_generator: Optional[BlockGenerator]
|
||||
if dummy_block_references and len(tx_block_heights) > 4:
|
||||
block_refs.extend(
|
||||
[
|
||||
tx_block_heights[1],
|
||||
tx_block_heights[len(tx_block_heights) // 2],
|
||||
tx_block_heights[-2],
|
||||
]
|
||||
)
|
||||
|
||||
new_gen: Optional[NewBlockGenerator]
|
||||
if transaction_data is not None:
|
||||
# this means the caller passed in transaction_data
|
||||
# to be included in the block.
|
||||
additions = compute_additions_unchecked(transaction_data)
|
||||
removals = transaction_data.removals()
|
||||
if start_height >= constants.HARD_FORK_HEIGHT:
|
||||
block_generator = simple_solution_generator_backrefs(transaction_data)
|
||||
block_refs = []
|
||||
program = simple_solution_generator_backrefs(transaction_data).program
|
||||
else:
|
||||
block_generator = simple_solution_generator(transaction_data)
|
||||
|
||||
aggregate_signature = transaction_data.aggregated_signature
|
||||
program = simple_solution_generator(transaction_data).program
|
||||
block_refs = []
|
||||
new_gen = NewBlockGenerator(
|
||||
program, [], block_refs, transaction_data.aggregated_signature, additions, removals
|
||||
)
|
||||
elif include_transactions:
|
||||
# if the caller did not pass in specific
|
||||
# transactions, this parameter means we just want
|
||||
# some transactions
|
||||
assert wallet is not None
|
||||
assert rng is not None
|
||||
transaction_data, additions = make_spend_bundle(available_coins, wallet, rng)
|
||||
removals = transaction_data.removals()
|
||||
program = simple_solution_generator(transaction_data).program
|
||||
new_gen = NewBlockGenerator(
|
||||
program, [], block_refs, transaction_data.aggregated_signature, additions, removals
|
||||
)
|
||||
transaction_data_included = False
|
||||
elif dummy_block_references:
|
||||
program = SerializedProgram.from_bytes(solution_generator([]))
|
||||
new_gen = NewBlockGenerator(program, [], block_refs, G2Element(), [], [])
|
||||
else:
|
||||
block_generator = None
|
||||
aggregate_signature = G2Element()
|
||||
new_gen = None
|
||||
|
||||
if dummy_block_references:
|
||||
if block_generator is None:
|
||||
program = SerializedProgram.from_bytes(solution_generator([]))
|
||||
block_generator = BlockGenerator(program, [])
|
||||
|
||||
if len(tx_block_heights) > 4:
|
||||
block_refs.extend(
|
||||
[
|
||||
tx_block_heights[1],
|
||||
tx_block_heights[len(tx_block_heights) // 2],
|
||||
tx_block_heights[-2],
|
||||
]
|
||||
)
|
||||
(
|
||||
full_block,
|
||||
block_record,
|
||||
@@ -838,10 +848,7 @@ class BlockTools:
|
||||
last_timestamp,
|
||||
start_height,
|
||||
time_per_block,
|
||||
block_generator,
|
||||
aggregate_signature,
|
||||
additions,
|
||||
removals,
|
||||
new_gen,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
@@ -854,7 +861,6 @@ class BlockTools:
|
||||
seed,
|
||||
normalized_to_identity_cc_ip=normalized_to_identity_cc_ip,
|
||||
current_time=current_time,
|
||||
block_refs=block_refs,
|
||||
)
|
||||
if block_record.is_transaction_block:
|
||||
transaction_data_included = True
|
||||
@@ -1033,19 +1039,8 @@ class BlockTools:
|
||||
latest_block_eos = latest_block
|
||||
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()
|
||||
additions = None
|
||||
removals = None
|
||||
if transaction_data_included:
|
||||
transaction_data = None
|
||||
if transaction_data is not None:
|
||||
additions = compute_additions_unchecked(transaction_data)
|
||||
removals = transaction_data.removals()
|
||||
elif include_transactions:
|
||||
assert wallet is not None
|
||||
assert rng is not None
|
||||
transaction_data, additions = make_spend_bundle(available_coins, wallet, rng)
|
||||
removals = transaction_data.removals()
|
||||
transaction_data_included = False
|
||||
sub_slots_finished += 1
|
||||
self.log.info(
|
||||
f"Sub slot finished. blocks included: {blocks_added_this_sub_slot} blocks_per_slot: "
|
||||
@@ -1113,30 +1108,47 @@ class BlockTools:
|
||||
pool_target = PoolTarget(pool_reward_puzzle_hash, uint32(0))
|
||||
else:
|
||||
pool_target = PoolTarget(self.pool_ph, uint32(0))
|
||||
|
||||
if dummy_block_references and len(tx_block_heights) > 4:
|
||||
block_refs.extend(
|
||||
[
|
||||
tx_block_heights[1],
|
||||
tx_block_heights[len(tx_block_heights) // 2],
|
||||
tx_block_heights[-2],
|
||||
]
|
||||
)
|
||||
|
||||
if transaction_data is not None:
|
||||
# this means the caller passed in transaction_data
|
||||
# to be included in the block.
|
||||
additions = compute_additions_unchecked(transaction_data)
|
||||
removals = transaction_data.removals()
|
||||
if start_height >= constants.HARD_FORK_HEIGHT:
|
||||
block_generator = simple_solution_generator_backrefs(transaction_data)
|
||||
block_refs = []
|
||||
program = simple_solution_generator_backrefs(transaction_data).program
|
||||
else:
|
||||
block_generator = simple_solution_generator(transaction_data)
|
||||
aggregate_signature = transaction_data.aggregated_signature
|
||||
program = simple_solution_generator(transaction_data).program
|
||||
block_refs = []
|
||||
new_gen = NewBlockGenerator(
|
||||
program, [], block_refs, transaction_data.aggregated_signature, additions, removals
|
||||
)
|
||||
elif include_transactions:
|
||||
# if the caller did not pass in specific
|
||||
# transactions, this parameter means we just want
|
||||
# some transactions
|
||||
assert wallet is not None
|
||||
assert rng is not None
|
||||
transaction_data, additions = make_spend_bundle(available_coins, wallet, rng)
|
||||
removals = transaction_data.removals()
|
||||
program = simple_solution_generator(transaction_data).program
|
||||
new_gen = NewBlockGenerator(
|
||||
program, [], block_refs, transaction_data.aggregated_signature, additions, removals
|
||||
)
|
||||
transaction_data_included = False
|
||||
elif dummy_block_references:
|
||||
program = SerializedProgram.from_bytes(solution_generator([]))
|
||||
new_gen = NewBlockGenerator(program, [], block_refs, G2Element(), [], [])
|
||||
else:
|
||||
block_generator = None
|
||||
aggregate_signature = G2Element()
|
||||
|
||||
if dummy_block_references:
|
||||
if block_generator is None:
|
||||
program = SerializedProgram.from_bytes(solution_generator([]))
|
||||
block_generator = BlockGenerator(program, [])
|
||||
|
||||
if len(tx_block_heights) > 4:
|
||||
block_refs.extend(
|
||||
[
|
||||
tx_block_heights[1],
|
||||
tx_block_heights[len(tx_block_heights) // 2],
|
||||
tx_block_heights[-2],
|
||||
]
|
||||
)
|
||||
new_gen = None
|
||||
|
||||
(
|
||||
full_block,
|
||||
@@ -1155,10 +1167,7 @@ class BlockTools:
|
||||
last_timestamp,
|
||||
start_height,
|
||||
time_per_block,
|
||||
block_generator,
|
||||
aggregate_signature,
|
||||
additions,
|
||||
removals,
|
||||
new_gen,
|
||||
height_to_hash,
|
||||
difficulty,
|
||||
required_iters,
|
||||
@@ -1173,7 +1182,6 @@ class BlockTools:
|
||||
overflow_rc_challenge=overflow_rc_challenge,
|
||||
normalized_to_identity_cc_ip=normalized_to_identity_cc_ip,
|
||||
current_time=current_time,
|
||||
block_refs=block_refs,
|
||||
)
|
||||
|
||||
if block_record.is_transaction_block:
|
||||
@@ -1788,10 +1796,7 @@ def get_full_block_and_block_record(
|
||||
last_timestamp: float,
|
||||
start_height: uint32,
|
||||
time_per_block: float,
|
||||
block_generator: Optional[BlockGenerator],
|
||||
aggregate_signature: G2Element,
|
||||
additions: Optional[list[Coin]],
|
||||
removals: Optional[list[Coin]],
|
||||
new_gen: Optional[NewBlockGenerator],
|
||||
height_to_hash: dict[uint32, bytes32],
|
||||
difficulty: uint64,
|
||||
required_iters: uint64,
|
||||
@@ -1803,7 +1808,6 @@ def get_full_block_and_block_record(
|
||||
prev_block: BlockRecord,
|
||||
seed: bytes = b"",
|
||||
*,
|
||||
block_refs: list[uint32] = [],
|
||||
overflow_cc_challenge: Optional[bytes32] = None,
|
||||
overflow_rc_challenge: Optional[bytes32] = None,
|
||||
normalized_to_identity_cc_ip: bool = False,
|
||||
@@ -1838,10 +1842,7 @@ def get_full_block_and_block_record(
|
||||
uint64(timestamp),
|
||||
BlockCache(blocks),
|
||||
seed,
|
||||
block_generator,
|
||||
aggregate_signature,
|
||||
additions,
|
||||
removals,
|
||||
new_gen,
|
||||
prev_block,
|
||||
finished_sub_slots,
|
||||
compute_cost=compute_cost_test,
|
||||
|
||||
@@ -47,9 +47,7 @@ class DedupCoinSpend:
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class UnspentLineageInfo:
|
||||
coin_id: bytes32
|
||||
coin_amount: uint64
|
||||
parent_id: bytes32
|
||||
parent_amount: uint64
|
||||
parent_parent_id: bytes32
|
||||
|
||||
|
||||
@@ -70,16 +68,19 @@ def set_next_singleton_version(
|
||||
next iteration
|
||||
"""
|
||||
singleton_child = next(
|
||||
(addition for addition in singleton_additions if addition.puzzle_hash == current_singleton.puzzle_hash), None
|
||||
(
|
||||
addition
|
||||
for addition in singleton_additions
|
||||
if addition.puzzle_hash == current_singleton.puzzle_hash and addition.amount == current_singleton.amount
|
||||
),
|
||||
None,
|
||||
)
|
||||
if singleton_child is None:
|
||||
raise ValueError("Could not find fast forward child singleton.")
|
||||
# Keep track of this in order to chain the next ff
|
||||
fast_forward_spends[current_singleton.puzzle_hash] = UnspentLineageInfo(
|
||||
coin_id=singleton_child.name(),
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=singleton_child.parent_coin_info,
|
||||
parent_amount=current_singleton.amount,
|
||||
parent_parent_id=current_singleton.parent_coin_info,
|
||||
)
|
||||
|
||||
@@ -107,14 +108,10 @@ def perform_the_fast_forward(
|
||||
ValueError if none of the additions are considered to be the singleton's
|
||||
next iteration
|
||||
"""
|
||||
new_coin = Coin(
|
||||
unspent_lineage_info.parent_id, spend_data.coin_spend.coin.puzzle_hash, unspent_lineage_info.coin_amount
|
||||
)
|
||||
new_parent = Coin(
|
||||
unspent_lineage_info.parent_parent_id,
|
||||
spend_data.coin_spend.coin.puzzle_hash,
|
||||
unspent_lineage_info.parent_amount,
|
||||
)
|
||||
singleton_ph = spend_data.coin_spend.coin.puzzle_hash
|
||||
singleton_amount = spend_data.coin_spend.coin.amount
|
||||
new_coin = Coin(unspent_lineage_info.parent_id, singleton_ph, singleton_amount)
|
||||
new_parent = Coin(unspent_lineage_info.parent_parent_id, singleton_ph, singleton_amount)
|
||||
# These hold because puzzle hash is not expected to change
|
||||
assert new_coin.name() == unspent_lineage_info.coin_id
|
||||
assert new_parent.name() == unspent_lineage_info.parent_id
|
||||
@@ -126,7 +123,7 @@ def perform_the_fast_forward(
|
||||
for addition in spend_data.additions:
|
||||
patched_addition = Coin(unspent_lineage_info.coin_id, addition.puzzle_hash, addition.amount)
|
||||
patched_additions.append(patched_addition)
|
||||
if addition.puzzle_hash == spend_data.coin_spend.coin.puzzle_hash:
|
||||
if addition.puzzle_hash == singleton_ph and addition.amount == singleton_amount:
|
||||
# We found the next version of this singleton
|
||||
singleton_child = patched_addition
|
||||
if singleton_child is None:
|
||||
@@ -135,9 +132,7 @@ def perform_the_fast_forward(
|
||||
# Keep track of this in order to chain the next ff
|
||||
fast_forward_spends[spend_data.coin_spend.coin.puzzle_hash] = UnspentLineageInfo(
|
||||
coin_id=singleton_child.name(),
|
||||
coin_amount=singleton_child.amount,
|
||||
parent_id=singleton_child.parent_coin_info,
|
||||
parent_amount=unspent_lineage_info.coin_amount,
|
||||
parent_parent_id=unspent_lineage_info.parent_id,
|
||||
)
|
||||
return new_coin_spend, patched_additions
|
||||
|
||||
@@ -2,12 +2,38 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from chia_rs import Coin, G2Element
|
||||
from chia_rs.sized_ints import uint32
|
||||
|
||||
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
||||
from chia.util.streamable import Streamable, streamable
|
||||
|
||||
|
||||
# This holds what we need to pre validate a block generator
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class BlockGenerator(Streamable):
|
||||
program: SerializedProgram = field(default_factory=SerializedProgram.default)
|
||||
# to run the block generator, we need the actual bytes of the previous
|
||||
# generators it may reference. These are parameters passed in to the block
|
||||
# generator
|
||||
generator_refs: list[bytes] = field(default_factory=list)
|
||||
|
||||
|
||||
# When we create a new block, this object holds the block generator and
|
||||
# additional information we need to create the UnfinishedBlock from it.
|
||||
# When creating a block, we still need to be able to run it, to compute its cost
|
||||
# and validate it. Therefore, this is a superset of the BlockGenerator class.
|
||||
@dataclass(frozen=True)
|
||||
class NewBlockGenerator(BlockGenerator):
|
||||
# when creating a block, we include the block heights of the generators we
|
||||
# reference. Tese are block heights and generator_refs contain the
|
||||
# corresponding bytes of the generator programs
|
||||
block_refs: list[uint32] = field(default_factory=list)
|
||||
# the aggregate signature of all AGG_SIG_* conditions returned by the block
|
||||
# generator.
|
||||
signature: G2Element = G2Element()
|
||||
# all CREATE_COIN outputs created by the block generator
|
||||
additions: list[Coin] = field(default_factory=list)
|
||||
# all coins being spent by the block generator
|
||||
removals: list[Coin] = field(default_factory=list)
|
||||
|
||||
Reference in New Issue
Block a user