1251 lines
43 KiB
Python
1251 lines
43 KiB
Python
import asyncio
|
|
import time
|
|
from typing import Optional
|
|
import pytest
|
|
from clvm.casts import int_to_bytes
|
|
|
|
from src.consensus.block_rewards import calculate_base_fee
|
|
from src.full_node.blockchain import ReceiveBlockResult
|
|
from src.types.condition_var_pair import ConditionVarPair
|
|
from src.types.condition_opcodes import ConditionOpcode
|
|
from src.types.header import HeaderData, Header
|
|
from src.types.sized_bytes import bytes32
|
|
from src.util.bundle_tools import best_solution_program
|
|
from src.server.outbound_message import OutboundMessage
|
|
from src.protocols import full_node_protocol
|
|
from src.types.full_block import FullBlock
|
|
from src.types.spend_bundle import SpendBundle
|
|
from src.util.errors import Err
|
|
from src.util.ints import uint64
|
|
from tests.block_tools import BlockTools
|
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
|
from tests.wallet_tools import WalletTool
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def event_loop():
|
|
loop = asyncio.get_event_loop()
|
|
yield loop
|
|
|
|
|
|
class TestBlockchainTransactions:
|
|
@pytest.fixture(scope="function")
|
|
async def two_nodes(self):
|
|
async for _ in setup_two_nodes({"COINBASE_FREEZE_PERIOD": 0}):
|
|
yield _
|
|
|
|
@pytest.fixture(scope="function")
|
|
async def two_nodes_standard_freeze(self):
|
|
async for _ in setup_two_nodes({"COINBASE_FREEZE_PERIOD": 200}):
|
|
yield _
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_basic_blockchain_tx(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
|
|
assert spend_bundle is not None
|
|
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
|
spend_bundle
|
|
)
|
|
async for _ in full_node_1.respond_transaction(tx):
|
|
outbound: OutboundMessage = _
|
|
# Maybe transaction means that it's accepted in mempool
|
|
assert outbound.message.function == "new_transaction"
|
|
|
|
sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
|
|
assert sb is spend_bundle
|
|
|
|
last_block = blocks[10]
|
|
next_spendbundle = await full_node_1.mempool_manager.create_bundle_for_tip(
|
|
last_block.header
|
|
)
|
|
assert next_spendbundle is not None
|
|
|
|
program = best_solution_program(next_spendbundle)
|
|
aggsig = next_spendbundle.aggregated_signature
|
|
|
|
dic_h = {11: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
next_block = new_blocks[11]
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(next_block)
|
|
):
|
|
pass
|
|
|
|
tips = full_node_1.blockchain.get_current_tips()
|
|
assert next_block.header in tips
|
|
|
|
added_coins = next_spendbundle.additions()
|
|
|
|
# Two coins are added, main spend and change
|
|
assert len(added_coins) == 2
|
|
for coin in added_coins:
|
|
unspent = await full_node_1.coin_store.get_coin_record(
|
|
coin.name(), next_block.header
|
|
)
|
|
assert unspent is not None
|
|
|
|
full_tips = await full_node_1.blockchain.get_full_tips()
|
|
in_full_tips = False
|
|
|
|
farmed_block: Optional[FullBlock] = None
|
|
for tip in full_tips:
|
|
if tip.header == next_block.header:
|
|
in_full_tips = True
|
|
farmed_block = tip
|
|
|
|
assert in_full_tips
|
|
assert farmed_block is not None
|
|
assert farmed_block.transactions_generator == program
|
|
assert farmed_block.header.data.aggregated_signature == aggsig
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_with_double_spend(self, two_nodes):
|
|
|
|
num_blocks = 5
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
spend_bundle_double = wallet_a.generate_signed_transaction(
|
|
1001, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {(num_blocks + 1): (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
next_block = new_blocks[num_blocks + 1]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.DOUBLE_SPEND
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_duplicate_output(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
spend_bundle_double = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {(num_blocks + 1): (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
next_block = new_blocks[(num_blocks + 1)]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.DUPLICATE_OUTPUT
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_with_reorg_double_spend(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {11: (program, aggsig)}
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, 10, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
# Move chain to height 20, with a spend at height 11
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Reorg at block 5, same spend at block 13 and 14 that was previously at block 11
|
|
dic_h = {13: (program, aggsig), 14: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
9,
|
|
blocks[:6],
|
|
10,
|
|
b"another seed",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
|
|
for block in new_blocks[:13]:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
next_block = new_blocks[13]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is None
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[13])
|
|
)
|
|
]
|
|
next_block = new_blocks[14]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is Err.DOUBLE_SPEND
|
|
|
|
# Now test Reorg at block 5, same spend at block 9 that was previously at block 11
|
|
dic_h = {9: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
4,
|
|
blocks[:6],
|
|
10,
|
|
b"another seed 2",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
for block in new_blocks[:9]:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
next_block = new_blocks[9]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is None
|
|
|
|
# Now test Reorg at block 10, same spend at block 11 that was previously at block 11
|
|
dic_h = {11: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
4,
|
|
blocks[:11],
|
|
10,
|
|
b"another seed 3",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
for block in new_blocks[:11]:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
next_block = new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is None
|
|
|
|
# Now test Reorg at block 11, same spend at block 12 that was previously at block 11
|
|
dic_h = {12: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
4,
|
|
blocks[:12],
|
|
10,
|
|
b"another seed 4",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
for block in new_blocks[:12]:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
next_block = new_blocks[12]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is Err.DOUBLE_SPEND
|
|
|
|
# Now test Reorg at block 11, same spend at block 15 that was previously at block 11
|
|
dic_h = {15: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
4,
|
|
blocks[:12],
|
|
10,
|
|
b"another seed 5",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
for block in new_blocks[:15]:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
next_block = new_blocks[15]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is Err.DOUBLE_SPEND
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_spend_reorg_coin(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_1_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_2_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_3_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_1_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {5: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
blocks[:5],
|
|
10,
|
|
b"spend_reorg_coin",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
coin_2 = None
|
|
for coin in new_blocks[-1].additions():
|
|
if coin.puzzle_hash == receiver_1_puzzlehash:
|
|
coin_2 = coin
|
|
break
|
|
assert coin_2 is not None
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_2_puzzlehash, coin_2
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {6: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
new_blocks[:6],
|
|
10,
|
|
b"spend_reorg_coin",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
coin_3 = None
|
|
for coin in new_blocks[-1].additions():
|
|
if coin.puzzle_hash == receiver_2_puzzlehash:
|
|
coin_3 = coin
|
|
break
|
|
assert coin_3 is not None
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_3_puzzlehash, coin_3
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {7: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
new_blocks[:7],
|
|
10,
|
|
b"spend_reorg_coin",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
coin_4 = None
|
|
for coin in new_blocks[-1].additions():
|
|
if coin.puzzle_hash == receiver_3_puzzlehash:
|
|
coin_4 = coin
|
|
break
|
|
assert coin_4 is not None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_spend_reorg_cb_coin(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_1_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Spends a coinbase created in reorg
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks[:6], 10, b"reorg cb coin", coinbase_puzzlehash
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
spent_block = new_blocks[-1]
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_1_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
spend_bundle_2 = wallet_a.generate_signed_transaction(
|
|
1000, receiver_1_puzzlehash, spent_block.header.data.fees_coin
|
|
)
|
|
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_2])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {7: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
new_blocks,
|
|
10,
|
|
b"reorg cb coin",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
new_blocks[-1], new_blocks[-1].header.data.fees_coin.amount
|
|
)
|
|
assert error is None
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
coins_created = []
|
|
for coin in new_blocks[-1].additions():
|
|
if coin.puzzle_hash == receiver_1_puzzlehash:
|
|
coins_created.append(coin)
|
|
assert len(coins_created) == 2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_spend_reorg_cb_coin_freeze(
|
|
self, two_nodes_standard_freeze
|
|
):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_1_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes_standard_freeze
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Spends a coinbase created in reorg
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks[:6], 10, b"reorg cb coin", coinbase_puzzlehash
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
spent_block = new_blocks[-1]
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_1_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {7: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
new_blocks,
|
|
10,
|
|
b"reorg cb coin",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
new_blocks[-1], new_blocks[-1].header.data.fees_coin.amount
|
|
)
|
|
assert error is Err.COINBASE_NOT_YET_SPENDABLE
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_blockchain_spend_reorg_since_genesis(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
receiver_1_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_1_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
block_spendbundle = SpendBundle.aggregate([spend_bundle])
|
|
program = best_solution_program(block_spendbundle)
|
|
aggsig = block_spendbundle.aggregated_signature
|
|
|
|
dic_h = {11: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(new_blocks[-1])
|
|
)
|
|
]
|
|
|
|
# Spends a coin in a genesis reorg, that was already spent
|
|
dic_h = {5: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
12,
|
|
[],
|
|
10,
|
|
b"reorg since genesis",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
)
|
|
for block in new_blocks:
|
|
[
|
|
_
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
)
|
|
]
|
|
assert new_blocks[-1].header_hash in full_node_1.blockchain.headers
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_my_coin_id(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
spent_block = blocks[1]
|
|
bad_block = blocks[2]
|
|
valid_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_MY_COIN_ID,
|
|
spent_block.header.data.coinbase.name(),
|
|
None,
|
|
)
|
|
valid_dic = {valid_cvp.opcode: [valid_cvp]}
|
|
bad_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_MY_COIN_ID,
|
|
bad_block.header.data.coinbase.name(),
|
|
None,
|
|
)
|
|
|
|
bad_dic = {bad_cvp.opcode: [bad_cvp]}
|
|
bad_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase, bad_dic
|
|
)
|
|
|
|
valid_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase, valid_dic
|
|
)
|
|
|
|
# Invalid block bundle
|
|
assert bad_spend_bundle is not None
|
|
invalid_program = best_solution_program(bad_spend_bundle)
|
|
aggsig = bad_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (invalid_program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate that block
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_MY_COIN_ID_FAILED
|
|
|
|
# Valid block bundle
|
|
assert valid_spend_bundle is not None
|
|
valid_program = best_solution_program(valid_spend_bundle)
|
|
aggsig = valid_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (valid_program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks[:11], 10, b"1", coinbase_puzzlehash, dic_h
|
|
)
|
|
next_block = new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
assert error is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_coin_consumed(self, two_nodes):
|
|
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
block1 = blocks[1]
|
|
block2 = blocks[2]
|
|
|
|
# This condition requires block2 coinbase to be spent
|
|
block1_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_COIN_CONSUMED,
|
|
block2.header.data.coinbase.name(),
|
|
None,
|
|
)
|
|
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
|
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
|
)
|
|
|
|
# This condition requires block1 coinbase to be spent
|
|
block2_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_COIN_CONSUMED,
|
|
block1.header.data.coinbase.name(),
|
|
None,
|
|
)
|
|
block2_dic = {block2_cvp.opcode: [block2_cvp]}
|
|
block2_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block2.header.data.coinbase, block2_dic
|
|
)
|
|
|
|
# Invalid block bundle
|
|
assert block1_spend_bundle is not None
|
|
solo_program = best_solution_program(block1_spend_bundle)
|
|
aggsig = block1_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (solo_program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate that block
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_COIN_CONSUMED_FAILED
|
|
|
|
# bundle_together contains both transactions
|
|
bundle_together = SpendBundle.aggregate(
|
|
[block1_spend_bundle, block2_spend_bundle]
|
|
)
|
|
valid_program = best_solution_program(bundle_together)
|
|
aggsig = bundle_together.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (valid_program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks[:11], 10, b"1", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate newly created block
|
|
next_block = new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_block_index_exceeds(self, two_nodes):
|
|
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
block1 = blocks[1]
|
|
|
|
# This condition requires block1 coinbase to be spent after index 11
|
|
block1_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS, int_to_bytes(11), None
|
|
)
|
|
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
|
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
|
)
|
|
|
|
# program that will be sent to early
|
|
assert block1_spend_bundle is not None
|
|
program = best_solution_program(block1_spend_bundle)
|
|
aggsig = block1_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate that block at index 11
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_BLOCK_INDEX_EXCEEDS_FAILED
|
|
|
|
dic_h = {12: (program, aggsig)}
|
|
valid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 2, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
for block in valid_new_blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Try to validate that block at index 12
|
|
next_block = valid_new_blocks[12]
|
|
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_block_age_exceeds(self, two_nodes):
|
|
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
block1 = blocks[1]
|
|
|
|
# This condition requires block1 coinbase to be spent more than 10 block after it was farmed
|
|
# block index has to be greater than (1 + 10 = 11)
|
|
block1_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS, int_to_bytes(10), None
|
|
)
|
|
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
|
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
|
)
|
|
|
|
# program that will be sent to early
|
|
assert block1_spend_bundle is not None
|
|
program = best_solution_program(block1_spend_bundle)
|
|
aggsig = block1_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate that block at index 11
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_BLOCK_AGE_EXCEEDS_FAILED
|
|
|
|
dic_h = {12: (program, aggsig)}
|
|
valid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 2, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
for block in valid_new_blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Try to validate that block at index 12
|
|
next_block = valid_new_blocks[12]
|
|
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_time_exceeds(self, two_nodes):
|
|
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
block1 = blocks[1]
|
|
|
|
# This condition requires block1 coinbase to be spent after 3 seconds from now
|
|
current_time_plus3 = uint64(int(time.time() * 1000) + 3000)
|
|
block1_cvp = ConditionVarPair(
|
|
ConditionOpcode.ASSERT_TIME_EXCEEDS, int_to_bytes(current_time_plus3), None
|
|
)
|
|
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
|
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
|
)
|
|
|
|
# program that will be sent to early
|
|
assert block1_spend_bundle is not None
|
|
program = best_solution_program(block1_spend_bundle)
|
|
aggsig = block1_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
# Try to validate that block before 3 sec
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_TIME_EXCEEDS_FAILED
|
|
|
|
# wait 3 sec to pass
|
|
await asyncio.sleep(3.1)
|
|
|
|
dic_h = {12: (program, aggsig)}
|
|
valid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 2, blocks[:11], 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
for block in valid_new_blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Try to validate that block after 3 sec have passed
|
|
next_block = valid_new_blocks[12]
|
|
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_filter(self, two_nodes):
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
spent_block = blocks[1]
|
|
|
|
spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
|
)
|
|
|
|
assert spend_bundle is not None
|
|
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
|
spend_bundle
|
|
)
|
|
async for _ in full_node_1.respond_transaction(tx):
|
|
outbound: OutboundMessage = _
|
|
# Maybe transaction means that it's accepted in mempool
|
|
assert outbound.message.function == "new_transaction"
|
|
|
|
sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
|
|
assert sb is spend_bundle
|
|
|
|
last_block = blocks[10]
|
|
next_spendbundle = await full_node_1.mempool_manager.create_bundle_for_tip(
|
|
last_block.header
|
|
)
|
|
assert next_spendbundle is not None
|
|
|
|
program = best_solution_program(next_spendbundle)
|
|
aggsig = next_spendbundle.aggregated_signature
|
|
|
|
dic_h = {11: (program, aggsig)}
|
|
new_blocks = bt.get_consecutive_blocks(
|
|
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
|
|
)
|
|
|
|
next_block = new_blocks[11]
|
|
|
|
bad_header = HeaderData(
|
|
next_block.header.data.height,
|
|
next_block.header.data.prev_header_hash,
|
|
next_block.header.data.timestamp,
|
|
bytes32(bytes([3] * 32)),
|
|
next_block.header.data.proof_of_space_hash,
|
|
next_block.header.data.weight,
|
|
next_block.header.data.total_iters,
|
|
next_block.header.data.additions_root,
|
|
next_block.header.data.removals_root,
|
|
next_block.header.data.coinbase,
|
|
next_block.header.data.coinbase_signature,
|
|
next_block.header.data.fees_coin,
|
|
next_block.header.data.aggregated_signature,
|
|
next_block.header.data.cost,
|
|
next_block.header.data.extension_data,
|
|
next_block.header.data.generator_hash,
|
|
)
|
|
bad_block = FullBlock(
|
|
next_block.proof_of_space,
|
|
next_block.proof_of_time,
|
|
Header(
|
|
bad_header,
|
|
bt.get_harvester_signature(
|
|
bad_header, next_block.proof_of_space.plot_pubkey
|
|
),
|
|
),
|
|
next_block.transactions_generator,
|
|
next_block.transactions_filter,
|
|
)
|
|
result, removed, error_code = await full_node_1.blockchain.receive_block(
|
|
bad_block
|
|
)
|
|
assert result == ReceiveBlockResult.INVALID_BLOCK
|
|
assert error_code == Err.INVALID_TRANSACTIONS_FILTER_HASH
|
|
|
|
result, removed, error_code = await full_node_1.blockchain.receive_block(
|
|
next_block
|
|
)
|
|
assert result == ReceiveBlockResult.ADDED_TO_HEAD
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_assert_fee_condition(self, two_nodes):
|
|
|
|
num_blocks = 10
|
|
wallet_a = WalletTool()
|
|
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
|
|
wallet_receiver = WalletTool()
|
|
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
|
|
|
# Farm blocks
|
|
blocks = bt.get_consecutive_blocks(
|
|
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
|
|
)
|
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
|
|
for block in blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|
|
|
|
# Coinbase that gets spent
|
|
block1 = blocks[1]
|
|
|
|
# This condition requires fee to be 10 mojo
|
|
cvp_fee = ConditionVarPair(ConditionOpcode.ASSERT_FEE, int_to_bytes(10), None)
|
|
block1_dic = {cvp_fee.opcode: [cvp_fee]}
|
|
# This spendbundle has 9 mojo as fee
|
|
invalid_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic, 9
|
|
)
|
|
|
|
assert invalid_spend_bundle is not None
|
|
program = best_solution_program(invalid_spend_bundle)
|
|
aggsig = invalid_spend_bundle.aggregated_signature
|
|
|
|
# Create another block that includes our transaction
|
|
dic_h = {11: (program, aggsig)}
|
|
invalid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
blocks,
|
|
10,
|
|
b"",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
fees=uint64(9),
|
|
)
|
|
|
|
# Try to validate that block at index 11
|
|
next_block = invalid_new_blocks[11]
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, next_block.header.data.fees_coin.amount
|
|
)
|
|
|
|
assert error is Err.ASSERT_FEE_CONDITION_FAILED
|
|
|
|
# This condition requires fee to be 10 mojo
|
|
cvp_fee = ConditionVarPair(ConditionOpcode.ASSERT_FEE, int_to_bytes(10), None)
|
|
condition_dict = {cvp_fee.opcode: [cvp_fee]}
|
|
valid_spend_bundle = wallet_a.generate_signed_transaction(
|
|
1000, receiver_puzzlehash, block1.header.data.coinbase, condition_dict, 10
|
|
)
|
|
|
|
assert valid_spend_bundle is not None
|
|
valid_program = best_solution_program(valid_spend_bundle)
|
|
aggsig = valid_spend_bundle.aggregated_signature
|
|
|
|
dic_h = {11: (valid_program, aggsig)}
|
|
valid_new_blocks = bt.get_consecutive_blocks(
|
|
test_constants,
|
|
1,
|
|
blocks[:11],
|
|
10,
|
|
b"",
|
|
coinbase_puzzlehash,
|
|
dic_h,
|
|
fees=uint64(10),
|
|
)
|
|
|
|
next_block = valid_new_blocks[11]
|
|
fee_base = calculate_base_fee(next_block.height)
|
|
error = await full_node_1.blockchain._validate_transactions(
|
|
next_block, fee_base
|
|
)
|
|
|
|
assert error is None
|
|
|
|
for block in valid_new_blocks:
|
|
async for _ in full_node_1.respond_block(
|
|
full_node_protocol.RespondBlock(block)
|
|
):
|
|
pass
|