New full node protocol

This commit is contained in:
Mariano Sorgente 2020-02-14 10:03:56 -08:00
parent 3baab5c6a7
commit 179ccbb1b7
No known key found for this signature in database
GPG Key ID: 0F866338C369278C
16 changed files with 1082 additions and 543 deletions

View File

@ -73,8 +73,8 @@ full_node:
enable_upnp: True
# Don't send any more than these number of headers and blocks, in one message
max_headers_to_send: 25
max_blocks_to_send: 5
max_headers_to_send: 1
max_blocks_to_send: 1
num_sync_batches: 10
# If node is more than these blocks behind, will do a sync

View File

@ -27,7 +27,7 @@ from src.util.ConsensusError import Err
from src.util.blockchain_check_conditions import blockchain_check_conditions_dict
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
from src.util.mempool_check_conditions import get_name_puzzle_conditions
from src.util.errors import BlockNotInBlockchain, InvalidGenesisBlock
from src.util.errors import InvalidGenesisBlock
from src.util.ints import uint32, uint64
from src.types.challenge import Challenge
@ -210,38 +210,6 @@ class Blockchain:
ret_hashes.append(curr.header_hash)
return list(reversed(ret_hashes))
def get_header_hashes_by_height(
self, heights: List[uint32], tip_header_hash: bytes32
) -> List[bytes32]:
"""
Returns a list of header blocks, one for each height requested.
"""
if len(heights) == 0:
return []
sorted_heights = sorted(
[(height, index) for index, height in enumerate(heights)], reverse=True
)
curr_block: Optional[Header] = self.headers.get(tip_header_hash, None)
if curr_block is None:
raise BlockNotInBlockchain(
f"Header hash {tip_header_hash} not present in chain."
)
headers: List[Tuple[int, Header]] = []
for height, index in sorted_heights:
if height > curr_block.height:
raise ValueError("Height is not valid for tip {tip_header_hash}")
while height < curr_block.height:
curr_block = self.headers.get(curr_block.prev_header_hash, None)
if curr_block is None:
raise ValueError(f"Do not have header {height}")
headers.append((index, curr_block))
# Return sorted by index (original order)
return [b.header_hash for _, b in sorted(headers, key=lambda pair: pair[0])]
def find_fork_point(self, alternate_chain: List[bytes32]) -> uint32:
"""
Takes in an alternate blockchain (headers), and compares it to self. Returns the last header

File diff suppressed because it is too large Load Diff

View File

@ -72,9 +72,22 @@ class MempoolManager:
else:
return None
async def is_fee_enough(self, fees: uint64, cost: uint64) -> bool:
"""
Determines whether any of the pools can accept a transaction with a given fees
and cost.
"""
if fees < 0:
return False
fees_per_cost = fees / cost
for pool in self.mempools.values():
if not pool.at_full_capacity() or fees_per_cost >= pool.get_min_fee_rate():
return True
return False
async def add_spendbundle(
self, new_spend: SpendBundle, to_pool: Mempool = None
) -> Tuple[bool, Optional[Err]]:
) -> Tuple[Optional[uint64], Optional[Err]]:
"""
Tries to add spendbundle to either self.mempools or to_pool if it's specified.
Returns true if it's added in any of pools, Returns error if it fails.
@ -86,12 +99,12 @@ class MempoolManager:
# npc contains names of the coins removed, puzzle_hashes and their spend conditions
fail_reason, npc_list, cost = await get_name_puzzle_conditions(program)
if fail_reason:
return False, fail_reason
return None, fail_reason
fees = new_spend.fees()
if cost == 0:
return False, Err.UNKNOWN
return None, Err.UNKNOWN
fees_per_cost: float = fees / cost
# build removal list
@ -101,13 +114,13 @@ class MempoolManager:
# Check additions for max coin amount
for coin in additions:
if coin.amount >= consensus_constants["MAX_COIN_AMOUNT"]:
return False, Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
return None, Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
# Watch out for duplicate outputs
addition_counter = collections.Counter(_.name() for _ in additions)
for k, v in addition_counter.items():
if v > 1:
return False, Err.DUPLICATE_OUTPUT
return None, Err.DUPLICATE_OUTPUT
# Spend might be valid for on pool but not for others
added_count = 0
@ -163,7 +176,7 @@ class MempoolManager:
for unspent in unspents.values():
coin = removals_dic[unspent.coin.name()]
if unspent.coin.puzzle_hash != coin.puzzle_hash:
return False, Err.WRONG_PUZZLE_HASH
return None, Err.WRONG_PUZZLE_HASH
# Verify conditions, create hash_key list for aggsig check
hash_key_pairs = []
@ -189,7 +202,7 @@ class MempoolManager:
# Verify aggregated signature
if not new_spend.aggregated_signature.validate(hash_key_pairs):
return False, Err.BAD_AGGREGATE_SIGNATURE
return None, Err.BAD_AGGREGATE_SIGNATURE
# Remove all conflicting Coins and SpendBundles
if fail_reason:
@ -203,9 +216,9 @@ class MempoolManager:
added_count += 1
if added_count > 0:
return True, None
return uint64(cost), None
else:
return False, errors[0]
return None, errors[0]
async def check_removals(
self, additions: List[Coin], removals: List[Coin], mempool: Mempool

View File

@ -8,7 +8,7 @@ from src.types.peer_info import PeerInfo
from src.types.proof_of_time import ProofOfTime
from src.types.sized_bytes import bytes32
from src.util.cbor_message import cbor_message
from src.util.ints import uint32
from src.util.ints import uint32, uint64
"""
@ -18,8 +18,24 @@ Protocol between full nodes.
@dataclass(frozen=True)
@cbor_message
class TransactionId:
class NewTip:
height: uint32
weight: uint64
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class RemovingTip:
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class NewTransaction:
transaction_id: bytes32
cost: uint64
fees: uint64
@dataclass(frozen=True)
@ -30,34 +46,120 @@ class RequestTransaction:
@dataclass(frozen=True)
@cbor_message
class NewTransaction:
class RespondTransaction:
transaction: SpendBundle
@dataclass(frozen=True)
@cbor_message
class RejectTransactionRequest:
transaction_id: bytes32
@dataclass(frozen=True)
@cbor_message
class NewProofOfTime:
height: uint32
challenge_hash: bytes32
number_of_iterations: uint64
@dataclass(frozen=True)
@cbor_message
class RequestProofOfTime:
height: uint32
challenge_hash: bytes32
number_of_iterations: uint64
@dataclass(frozen=True)
@cbor_message
class RespondProofOfTime:
proof: ProofOfTime
@dataclass(frozen=True)
@cbor_message
class UnfinishedBlock:
block: FullBlock
class RejectProofOfTimeRequest:
challenge_hash: bytes32
iterations: uint64
@dataclass(frozen=True)
@cbor_message
class RequestBlock:
class NewCompactProofOfTime:
height: uint32
challenge_hash: bytes32
number_of_iterations: uint64
@dataclass(frozen=True)
@cbor_message
class RequestCompactProofOfTime:
height: uint32
challenge_hash: bytes32
number_of_iterations: uint64
@dataclass(frozen=True)
@cbor_message
class RespondCompactProofOfTime:
proof: ProofOfTime
@dataclass(frozen=True)
@cbor_message
class RejectCompactProofOfTimeRequest:
challenge_hash: bytes32
iterations: uint64
@dataclass(frozen=True)
@cbor_message
class NewUnfinishedBlock:
previous_header_hash: bytes32
iterations: uint64
new_header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class RequestUnfinishedBlock:
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class Block:
class RespondUnfinishedBlock:
block: FullBlock
@dataclass(frozen=True)
@cbor_message
class RejectUnfinishedBlockRequest:
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class RequestBlock:
height: uint32
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class RespondBlock:
block: FullBlock
@dataclass(frozen=True)
@cbor_message
class RejectBlockRequest:
height: uint32
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class RequestPeers:
@ -86,27 +188,19 @@ class AllHeaderHashes:
@dataclass(frozen=True)
@cbor_message
class RequestHeaderBlocks:
tip_header_hash: bytes32
heights: List[uint32]
class RequestHeaderBlock:
height: uint32
header_hash: bytes32
@dataclass(frozen=True)
@cbor_message
class HeaderBlocks:
tip_header_hash: bytes32
header_blocks: List[HeaderBlock]
class RespondHeaderBlock:
header_block: HeaderBlock
@dataclass(frozen=True)
@cbor_message
class RequestSyncBlocks:
tip_header_hash: bytes32
heights: List[uint32]
@dataclass(frozen=True)
@cbor_message
class SyncBlocks:
tip_header_hash: bytes32
blocks: List[FullBlock]
class RejectHeaderBlockRequest:
height: uint32
header_hash: bytes32

View File

@ -19,6 +19,8 @@ class FullNodeStore:
db: aiosqlite.Connection
# Whether or not we are syncing
sync_mode: bool
# Whether we are waiting for tips (at the start of sync) or already syncing
waiting_for_tips: bool
# Potential new tips that we have received from others.
potential_tips: Dict[bytes32, FullBlock]
# List of all header hashes up to the tip, download up front
@ -35,6 +37,8 @@ class FullNodeStore:
potential_future_blocks: List[FullBlock]
# Current estimate of the speed of the network timelords
proof_of_time_estimate_ips: uint64
# Proof of time heights
proof_of_time_heights: Dict[Tuple[bytes32, uint64], uint32]
# Our best unfinished block
unfinished_blocks_leader: Tuple[uint32, uint64]
# Blocks which we have created, but don't have proof of space yet, old ones are cleared
@ -45,6 +49,7 @@ class FullNodeStore:
seen_unfinished_blocks: set
# Blocks which we have received but our blockchain does not reach, old ones are cleared
disconnected_blocks: Dict[bytes32, FullBlock]
# Lock
lock: asyncio.Lock
@ -80,6 +85,7 @@ class FullNodeStore:
await self.db.commit()
self.sync_mode = False
self.waiting_for_tips = True
self.potential_tips = {}
self.potential_hashes = []
self.potential_headers = {}
@ -88,6 +94,7 @@ class FullNodeStore:
self.potential_blocks_received = {}
self.potential_future_blocks = []
self.proof_of_time_estimate_ips = uint64(10000)
self.proof_of_time_heights = {}
self.unfinished_blocks_leader = (
uint32(0),
uint64((1 << 64) - 1),
@ -132,6 +139,22 @@ class FullNodeStore:
return FullBlock.from_bytes(row[2])
return None
async def get_blocks_at(self, heights: List[uint32]) -> List[FullBlock]:
if len(heights) == 0:
return []
heights_db = tuple(heights)
formatted_str = (
f'SELECT * from blocks WHERE height in ({"?," * (len(heights_db) - 1)}?)'
)
cursor = await self.db.execute(formatted_str, heights_db)
rows = await cursor.fetchall()
await cursor.close()
blocks: List[FullBlock] = []
for row in rows:
blocks.append(FullBlock.from_bytes(row[2]))
return blocks
async def get_headers(self) -> List[Header]:
cursor = await self.db.execute("SELECT * from headers")
rows = await cursor.fetchall()
@ -181,6 +204,12 @@ class FullNodeStore:
def get_sync_mode(self) -> bool:
return self.sync_mode
def set_waiting_for_tips(self, waiting_for_tips: bool) -> None:
self.waiting_for_tips = waiting_for_tips
def get_waiting_for_tips(self) -> bool:
return self.waiting_for_tips
async def clear_sync_info(self):
self.potential_tips.clear()
self.potential_headers.clear()
@ -188,6 +217,7 @@ class FullNodeStore:
await cursor.close()
self.potential_blocks_received.clear()
self.potential_future_blocks.clear()
self.waiting_for_tips = True
def get_potential_tips_tuples(self) -> List[Tuple[bytes32, FullBlock]]:
return list(self.potential_tips.items())
@ -256,9 +286,15 @@ class FullNodeStore:
return (res[0], res[1], res[2])
def clear_candidate_blocks_below(self, height: uint32) -> None:
for key in list(self.candidate_blocks.keys()):
if self.candidate_blocks[key][3] < height:
del_keys = []
for key, value in self.candidate_blocks.items():
if value[3] < height:
del_keys.append(key)
for key in del_keys:
try:
del self.candidate_blocks[key]
except KeyError:
pass
def add_unfinished_block(
self, key: Tuple[bytes32, uint64], block: FullBlock
@ -281,9 +317,15 @@ class FullNodeStore:
return self.unfinished_blocks.copy()
def clear_unfinished_blocks_below(self, height: uint32) -> None:
for key in list(self.unfinished_blocks.keys()):
if self.unfinished_blocks[key].height < height:
del_keys = []
for key, unf in self.unfinished_blocks.items():
if unf.height < height:
del_keys.append(key)
for key in del_keys:
try:
del self.unfinished_blocks[key]
except KeyError:
pass
def set_unfinished_block_leader(self, key: Tuple[bytes32, uint64]) -> None:
self.unfinished_blocks_leader = key
@ -296,3 +338,24 @@ class FullNodeStore:
def get_proof_of_time_estimate_ips(self) -> uint64:
return self.proof_of_time_estimate_ips
def add_proof_of_time_heights(
self, challenge_iters: Tuple[bytes32, uint64], height: uint32
) -> None:
self.proof_of_time_heights[challenge_iters] = height
def get_proof_of_time_heights(
self, challenge_iters: Tuple[bytes32, uint64]
) -> Optional[uint32]:
return self.proof_of_time_heights.get(challenge_iters, None)
def clear_proof_of_time_heights_below(self, height: uint32) -> None:
del_keys: List = []
for key, value in self.proof_of_time_heights.items():
if value < height:
del_keys.append(key)
for key in del_keys:
try:
del self.proof_of_time_heights[key]
except KeyError:
pass

View File

@ -34,4 +34,4 @@ class ProofOfSpace(Streamable):
@staticmethod
def calculate_plot_seed(pool_pubkey: PublicKey, plot_pubkey: PublicKey) -> bytes32:
return bytes32(std_hash(bytes(pool_pubkey) + bytes(plot_pubkey)).digest())
return bytes32(std_hash(bytes(pool_pubkey) + bytes(plot_pubkey)))

View File

@ -116,9 +116,7 @@ class BlockTools:
block_list.append(FullBlock.from_bytes(test_constants["GENESIS_BLOCK"]))
else:
block_list.append(
self.create_genesis_block(
test_constants, std_hash(seed), seed
)
self.create_genesis_block(test_constants, std_hash(seed), seed)
)
prev_difficulty = test_constants["DIFFICULTY_STARTING"]
curr_difficulty = prev_difficulty

View File

@ -15,8 +15,7 @@ from src.types.hashable.Coin import Coin
from src.types.header import Header, HeaderData
from src.types.proof_of_space import ProofOfSpace
from src.coin_store import CoinStore
from src.util.ints import uint8, uint32, uint64
from src.util.errors import BlockNotInBlockchain
from src.util.ints import uint8, uint64
from tests.block_tools import BlockTools
bt = BlockTools()
@ -52,9 +51,6 @@ class TestGenesisBlock:
assert len(bc1.get_current_tips()) == 1
genesis_block = bc1.get_current_tips()[0]
assert genesis_block.height == 0
assert (
bc1.get_header_hashes_by_height([uint32(0)], genesis_block.header_hash)
)[0] == genesis_block.header_hash
assert (
bc1.get_next_difficulty(genesis_block.header_hash)
) == genesis_block.weight
@ -83,32 +79,6 @@ class TestBlockValidation:
await unspent_store.close()
await store.close()
@pytest.mark.asyncio
async def test_get_header_hashes(self, initial_blockchain):
blocks, b = initial_blockchain
header_hashes_1 = b.get_header_hashes_by_height(
[0, 8, 3], blocks[8].header_hash
)
assert header_hashes_1 == [
blocks[0].header_hash,
blocks[8].header_hash,
blocks[3].header_hash,
]
try:
b.get_header_hashes_by_height([0, 8, 3], blocks[6].header_hash)
thrown = False
except ValueError:
thrown = True
assert thrown
try:
b.get_header_hashes_by_height([0, 8, 3], blocks[9].header_hash)
thrown_2 = False
except BlockNotInBlockchain:
thrown_2 = True
assert thrown_2
@pytest.mark.asyncio
async def test_prev_pointer(self, initial_blockchain):
blocks, b = initial_blockchain

View File

@ -48,7 +48,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spent_block = blocks[1]
@ -58,13 +60,13 @@ class TestBlockchainTransactions:
)
assert spend_bundle is not None
tx: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle
)
async for _ in full_node_1.transaction(tx):
async for _ in full_node_1.respond_transaction(tx):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb = await full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
assert sb is spend_bundle
@ -84,7 +86,9 @@ class TestBlockchainTransactions:
)
next_block = new_blocks[11]
async for _ in full_node_1.block(full_node_protocol.Block(next_block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(next_block)
):
pass
tips = full_node_1.blockchain.get_current_tips()
@ -129,7 +133,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spent_block = blocks[1]
@ -172,7 +178,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spent_block = blocks[1]
@ -216,7 +224,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Coinbase that gets spent
@ -290,7 +300,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Coinbase that gets spent
@ -371,7 +383,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Coinbase that gets spent
@ -411,7 +425,9 @@ class TestBlockchainTransactions:
)
for block in valid_new_blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Try to validate that block at index 12
@ -439,7 +455,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Coinbase that gets spent
@ -480,7 +498,9 @@ class TestBlockchainTransactions:
)
for block in valid_new_blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Try to validate that block at index 12
@ -508,7 +528,9 @@ class TestBlockchainTransactions:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Coinbase that gets spent
@ -552,7 +574,9 @@ class TestBlockchainTransactions:
)
for block in valid_new_blocks:
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
# Try to validate that block after 3 sec have passed

38
tests/test_full_node.py Normal file
View File

@ -0,0 +1,38 @@
# import asyncio
# import pytest
# from src.protocols import full_node_protocol
# from src.types.peer_info import PeerInfo
# from src.util.ints import uint16
# from tests.setup_nodes import setup_two_nodes, test_constants, bt
# @pytest.fixture(scope="module")
# def event_loop():
# loop = asyncio.get_event_loop()
# yield loop
# class TestFullNode:
# @pytest.fixture(scope="function")
# async def two_nodes(self):
# async for _ in setup_two_nodes():
# yield _
#
# @pytest.mark.asyncio
# async def test_unfinished_blocks_load(self, two_nodes):
# num_blocks = 10
# full_node_1, full_node_2, server_1, server_2 = two_nodes
# blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
# for i in range(1, num_blocks - 1):
# async for _ in full_node_1.respond_block(
# full_node_protocol.RespondBlock(blocks[i])
# ):
# pass
# await server_2.start_client(
# PeerInfo(server_1._host, uint16(server_1._port)), None
# )
# await asyncio.sleep(2) # Allow connections to get made

View File

@ -28,7 +28,9 @@ class TestFullSync:
full_node_1, full_node_2, server_1, server_2 = two_nodes
for i in range(1, num_blocks):
async for _ in full_node_1.block(full_node_protocol.Block(blocks[i])):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(blocks[i])
):
pass
await server_2.start_client(
@ -64,15 +66,21 @@ class TestFullSync:
# 10 blocks to node_1
for i in range(1, num_blocks):
async for _ in full_node_1.block(full_node_protocol.Block(blocks[i])):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(blocks[i])
):
pass
# 4 different blocks to node_2
for i in range(1, num_blocks_2):
async for _ in full_node_2.block(full_node_protocol.Block(blocks_2[i])):
async for _ in full_node_2.respond_block(
full_node_protocol.RespondBlock(blocks_2[i])
):
pass
# 6th block from node_1 to node_2
async for _ in full_node_2.block(full_node_protocol.Block(blocks[5])):
async for _ in full_node_2.respond_block(
full_node_protocol.RespondBlock(blocks[5])
):
pass
await server_2.start_client(

View File

@ -44,20 +44,22 @@ class TestMempool:
block = blocks[1]
print(f"block coinbase: {block.body.coinbase.name()}")
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spend_bundle = wallet_a.generate_signed_transaction(
1000, receiver_puzzlehash, block.body.coinbase
)
assert spend_bundle is not None
tx: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle
)
async for _ in full_node_1.transaction(tx):
async for _ in full_node_1.respond_transaction(tx):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb = await full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
assert sb is spend_bundle
@ -76,21 +78,23 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes_standard_freeze
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spend_bundle = wallet_a.generate_signed_transaction(
1000, receiver_puzzlehash, block.body.coinbase
)
assert spend_bundle is not None
tx: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle
)
async for _ in full_node_1.transaction(tx):
async for _ in full_node_1.respond_transaction(tx):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function != "maybe_transaction"
assert outbound.message.function != "new_transaction"
sb = await full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
assert sb is None
@ -100,13 +104,15 @@ class TestMempool:
)
for i in range(1, 201):
async for _ in full_node_1.block(full_node_protocol.Block(blocks[i])):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(blocks[i])
):
pass
async for _ in full_node_1.transaction(tx):
async for _ in full_node_1.respond_transaction(tx):
outbound_2: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound_2.message.function == "maybe_transaction"
assert outbound_2.message.function == "new_transaction"
print(blocks[1].body.coinbase.name())
sb = await full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
assert sb is spend_bundle
@ -125,7 +131,9 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spend_bundle1 = wallet_a.generate_signed_transaction(
@ -133,23 +141,23 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
other_receiver = WalletTool()
spend_bundle2 = wallet_a.generate_signed_transaction(
1000, other_receiver.get_new_puzzlehash(), block.body.coinbase
)
assert spend_bundle2 is not None
tx2: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle2
)
async for _ in full_node_1.transaction(tx2):
async for _ in full_node_1.respond_transaction(tx2):
pass
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -172,30 +180,32 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
spend_bundle1 = wallet_a.generate_signed_transaction(
1000, receiver_puzzlehash, block.body.coinbase
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
spend_bundle2 = wallet_a.generate_signed_transaction(
1000, receiver_puzzlehash, block.body.coinbase, fee=1
)
assert spend_bundle2 is not None
tx2: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle2
)
async for _ in full_node_1.transaction(tx2):
async for _ in full_node_1.respond_transaction(tx2):
pass
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -218,7 +228,9 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
cvp = ConditionVarPair(
@ -233,13 +245,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function != "maybe_transaction"
assert outbound.message.function != "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -259,7 +271,9 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
cvp = ConditionVarPair(
@ -274,13 +288,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -300,7 +314,9 @@ class TestMempool:
full_node_1, full_node_2, server_1, server_2 = two_nodes
block = blocks[1]
async for _ in full_node_1.block(full_node_protocol.Block(block)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(block)
):
pass
cvp = ConditionVarPair(
@ -313,13 +329,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function != "maybe_transaction"
assert outbound.message.function != "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -341,7 +357,9 @@ class TestMempool:
block = blocks[1]
for b in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(b)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(b)
):
pass
cvp = ConditionVarPair(
@ -354,13 +372,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -382,7 +400,9 @@ class TestMempool:
block = blocks[1]
for b in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(b)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(b)
):
pass
cvp = ConditionVarPair(
@ -395,13 +415,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -423,7 +443,9 @@ class TestMempool:
block = blocks[1]
for b in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(b)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(b)
):
pass
cvp = ConditionVarPair(
@ -436,13 +458,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function != "maybe_transaction"
assert outbound.message.function != "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -464,7 +486,9 @@ class TestMempool:
block = blocks[1]
for b in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(b)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(b)
):
pass
time_now = uint64(int(time() * 1000))
@ -479,13 +503,13 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound.message.function == "maybe_transaction"
assert outbound.message.function == "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -507,7 +531,9 @@ class TestMempool:
block = blocks[1]
for b in blocks:
async for _ in full_node_1.block(full_node_protocol.Block(b)):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(b)
):
pass
time_now = uint64(int(time() * 1000))
@ -525,12 +551,12 @@ class TestMempool:
)
assert spend_bundle1 is not None
tx1: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx1):
async for _ in full_node_1.respond_transaction(tx1):
outbound: OutboundMessage = _
assert outbound.message.function != "maybe_transaction"
assert outbound.message.function != "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())
@ -538,13 +564,13 @@ class TestMempool:
# Sleep so that 3 sec passes
await asyncio.sleep(3)
tx2: full_node_protocol.NewTransaction = full_node_protocol.NewTransaction(
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
spend_bundle1
)
async for _ in full_node_1.transaction(tx2):
async for _ in full_node_1.respond_transaction(tx2):
outbound_2: OutboundMessage = _
# Maybe transaction means that it's accepted in mempool
assert outbound_2.message.function == "maybe_transaction"
assert outbound_2.message.function == "new_transaction"
sb1 = await full_node_1.mempool_manager.get_spendbundle(spend_bundle1.name())

View File

@ -30,7 +30,9 @@ class TestNodeLoad:
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
for i in range(1, num_blocks - 1):
async for _ in full_node_1.block(full_node_protocol.Block(blocks[i])):
async for _ in full_node_1.respond_block(
full_node_protocol.RespondBlock(blocks[i])
):
pass
await server_2.start_client(
@ -43,14 +45,15 @@ class TestNodeLoad:
start_unf = time.time()
for i in range(num_unfinished_blocks):
msg = Message(
"unfinished_block", full_node_protocol.UnfinishedBlock(blocks[9])
"respond_unfinished_block",
full_node_protocol.RespondUnfinishedBlock(blocks[9]),
)
server_1.push_message(
OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST)
)
# Send the whole block ast the end so we can detect when the node is done
block_msg = Message("block", full_node_protocol.Block(blocks[9]))
block_msg = Message("respond_block", full_node_protocol.RespondBlock(blocks[9]))
server_1.push_message(
OutboundMessage(NodeType.FULL_NODE, block_msg, Delivery.BROADCAST)
)
@ -82,7 +85,7 @@ class TestNodeLoad:
start_unf = time.time()
for i in range(1, num_blocks):
msg = Message("block", full_node_protocol.Block(blocks[i]))
msg = Message("respond_block", full_node_protocol.RespondBlock(blocks[i]))
server_1.push_message(
OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST)
)

View File

@ -17,7 +17,7 @@ def test_1():
conditions = [
make_create_coin_condition(std_hash(bytes(pp)), amount)
for pp, amount in [(puzzle_program_1, 1000), (puzzle_program_2, 2000), ]
for pp, amount in [(puzzle_program_1, 1000), (puzzle_program_2, 2000),]
]
assert conditions is not None

View File

@ -41,6 +41,7 @@ class TestStore:
async def test_basic_store(self):
assert sqlite3.threadsafety == 1
blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0")
blocks_alt = bt.get_consecutive_blocks(test_constants, 3, [], 9, b"1")
db_filename = Path("blockchain_test.db")
db_filename_2 = Path("blockchain_test_2.db")
db_filename_3 = Path("blockchain_test_3.db")
@ -64,8 +65,11 @@ class TestStore:
await db.add_block(block)
assert block == await db.get_block(block.header_hash)
# Get headers
assert len(await db.get_headers()) == len(blocks)
await db.add_block(blocks_alt[2])
assert len(await db.get_blocks_at([1, 2])) == 3
# Get headers (added alt block also, so +1)
assert len(await db.get_headers()) == len(blocks) + 1
# Save/get sync
for sync_mode in (False, True):
@ -95,6 +99,16 @@ class TestStore:
db.clear_candidate_blocks_below(uint32(8))
assert db.get_candidate_block(blocks[5].header_hash) is None
# Proof of time heights
chall_iters = (bytes32(bytes([1] * 32)), uint32(32532535))
chall_iters_2 = (bytes32(bytes([3] * 32)), uint32(12522535))
assert db.get_proof_of_time_heights(chall_iters) is None
db.add_proof_of_time_heights(chall_iters, 5)
db.add_proof_of_time_heights(chall_iters_2, 7)
db.clear_proof_of_time_heights_below(6)
assert db.get_proof_of_time_heights(chall_iters) is None
assert db.get_proof_of_time_heights(chall_iters_2) is not None
# Add/get unfinished block
i = 1
for block in blocks: