mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 16:08:51 +03:00
Complete removal of body
This commit is contained in:
parent
f460480406
commit
3b0b7dfb72
@ -31,6 +31,7 @@ from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
||||
from src.util.errors import InvalidGenesisBlock
|
||||
from src.util.ints import uint32, uint64
|
||||
from src.types.challenge import Challenge
|
||||
from src.util.hash import std_hash
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -511,16 +512,12 @@ class Blockchain:
|
||||
if block.proof_of_space.get_hash() != block.header.data.proof_of_space_hash:
|
||||
return False
|
||||
|
||||
# 2. Check body hash
|
||||
if block.body.get_hash() != block.header.data.body_hash:
|
||||
return False
|
||||
|
||||
# 3. Check coinbase signature with pool pk
|
||||
pair = block.body.coinbase_signature.PkMessagePair(
|
||||
block.proof_of_space.pool_pubkey, block.body.coinbase.name(),
|
||||
pair = block.header.data.coinbase_signature.PkMessagePair(
|
||||
block.proof_of_space.pool_pubkey, block.header.data.coinbase.name(),
|
||||
)
|
||||
|
||||
if not block.body.coinbase_signature.validate([pair]):
|
||||
if not block.header.data.coinbase_signature.validate([pair]):
|
||||
return False
|
||||
|
||||
# 4. Check harvester signature of header data is valid based on harvester key
|
||||
@ -559,7 +556,10 @@ class Blockchain:
|
||||
):
|
||||
return False
|
||||
|
||||
# 7. Check filter hash is correct TODO
|
||||
# 7. Check filter hash is correct
|
||||
if block.header.data.filter_hash != bytes32([0] * 32):
|
||||
if std_hash(block.transactions_filter) != block.header.data.filter_hash:
|
||||
return False
|
||||
|
||||
# 8. Check extension data, if any is added
|
||||
|
||||
@ -681,19 +681,19 @@ class Blockchain:
|
||||
return False
|
||||
|
||||
coinbase_reward = calculate_block_reward(block.height)
|
||||
if coinbase_reward != block.body.coinbase.amount:
|
||||
if coinbase_reward != block.header.data.coinbase.amount:
|
||||
return False
|
||||
fee_base = calculate_base_fee(block.height)
|
||||
|
||||
# 8 Validate transactions
|
||||
# target reward_fee = 1/8 coinbase reward + tx fees
|
||||
if block.body.transactions:
|
||||
if block.transactions_generator:
|
||||
# Validate transactions, and verify that fee_base + TX fees = fee_coin.amount
|
||||
err = await self._validate_transactions(block, fee_base)
|
||||
if err:
|
||||
return False
|
||||
else:
|
||||
if fee_base != block.body.fees_coin.amount:
|
||||
if fee_base != block.header.data.fees_coin.amount:
|
||||
return False
|
||||
root_error = self._validate_merkle_root(block)
|
||||
if root_error:
|
||||
@ -932,8 +932,8 @@ class Blockchain:
|
||||
if tx_removals:
|
||||
removals.extend(tx_removals)
|
||||
|
||||
additions.append(block.body.coinbase)
|
||||
additions.append(block.body.fees_coin)
|
||||
additions.append(block.header.data.coinbase)
|
||||
additions.append(block.header.data.fees_coin)
|
||||
|
||||
removal_merkle_set = MerkleSet()
|
||||
addition_merkle_set = MerkleSet()
|
||||
@ -969,10 +969,10 @@ class Blockchain:
|
||||
self, block: FullBlock, fee_base: uint64
|
||||
) -> Optional[Err]:
|
||||
|
||||
if not block.body.transactions:
|
||||
if not block.transactions_generator:
|
||||
return Err.UNKNOWN
|
||||
# Get List of names removed, puzzles hashes for removed coins and conditions crated
|
||||
error, npc_list, cost = get_name_puzzle_conditions(block.body.transactions)
|
||||
error, npc_list, cost = get_name_puzzle_conditions(block.transactions_generator)
|
||||
|
||||
if cost > self.constants["MAX_BLOCK_COST"]:
|
||||
return Err.BLOCK_COST_EXCEEDS_MAX
|
||||
@ -1004,6 +1004,8 @@ class Blockchain:
|
||||
if root_error:
|
||||
return root_error
|
||||
|
||||
# TODO(straya): validate filter
|
||||
|
||||
# Watch out for duplicate outputs
|
||||
addition_counter = collections.Counter(_.name() for _ in additions)
|
||||
for k, v in addition_counter.items():
|
||||
@ -1055,7 +1057,7 @@ class Blockchain:
|
||||
fees = removed - added
|
||||
|
||||
# Check coinbase reward
|
||||
if fees + fee_base != block.body.fees_coin.amount:
|
||||
if fees + fee_base != block.header.data.fees_coin.amount:
|
||||
return Err.BAD_COINBASE_REWARD
|
||||
|
||||
# Verify that removed coin puzzle_hashes match with calculated puzzle_hashes
|
||||
@ -1077,9 +1079,9 @@ class Blockchain:
|
||||
)
|
||||
|
||||
# Verify aggregated signature
|
||||
if not block.body.aggregated_signature:
|
||||
if not block.header.data.aggregated_signature:
|
||||
return Err.BAD_AGGREGATE_SIGNATURE
|
||||
if not block.body.aggregated_signature.validate(hash_key_pairs):
|
||||
if not block.header.data.aggregated_signature.validate(hash_key_pairs):
|
||||
return Err.BAD_AGGREGATE_SIGNATURE
|
||||
|
||||
return None
|
||||
|
@ -106,10 +106,10 @@ class CoinStore:
|
||||
await self.set_spent(coin_name, block.height)
|
||||
|
||||
coinbase: CoinRecord = CoinRecord(
|
||||
block.body.coinbase, block.height, uint32(0), False, True
|
||||
block.header.data.coinbase, block.height, uint32(0), False, True
|
||||
)
|
||||
fees_coin: CoinRecord = CoinRecord(
|
||||
block.body.fees_coin, block.height, uint32(0), False, True
|
||||
block.header.data.fees_coin, block.height, uint32(0), False, True
|
||||
)
|
||||
|
||||
await self.add_coin_record(coinbase)
|
||||
@ -142,9 +142,9 @@ class CoinStore:
|
||||
added: CoinRecord = CoinRecord(coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
diff_store.diffs[added.name.hex()] = added
|
||||
|
||||
coinbase: CoinRecord = CoinRecord(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
coinbase: CoinRecord = CoinRecord(block.header.data.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
diff_store.diffs[coinbase.name.hex()] = coinbase
|
||||
fees_coin: CoinRecord = CoinRecord(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
fees_coin: CoinRecord = CoinRecord(block.header.data.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
diff_store.diffs[fees_coin.name.hex()] = fees_coin
|
||||
|
||||
for coin_name in removals:
|
||||
|
@ -22,7 +22,6 @@ from src.util.bundle_tools import best_solution_program
|
||||
from src.full_node.mempool_manager import MempoolManager
|
||||
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
|
||||
from src.server.server import ChiaServer
|
||||
from src.types.body import Body
|
||||
from src.types.challenge import Challenge
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.hashable.coin import Coin, hash_coin_list
|
||||
@ -1234,20 +1233,12 @@ class FullNode:
|
||||
extension_data: bytes32 = bytes32([0] * 32)
|
||||
|
||||
# Creates a block with transactions, coinbase, and fees
|
||||
body: Body = Body(
|
||||
request.coinbase,
|
||||
request.coinbase_signature,
|
||||
fees_coin,
|
||||
solution_program,
|
||||
aggregate_sig,
|
||||
cost,
|
||||
extension_data,
|
||||
)
|
||||
# Creates the block header
|
||||
prev_header_hash: bytes32 = target_tip.get_hash()
|
||||
timestamp: uint64 = uint64(int(time.time()))
|
||||
|
||||
# Create filter
|
||||
encoded_filter: Optional[bytes] = None
|
||||
byte_array_tx: List[bytes32] = []
|
||||
if spend_bundle:
|
||||
additions: List[Coin] = spend_bundle.additions()
|
||||
@ -1256,14 +1247,11 @@ class FullNode:
|
||||
byte_array_tx.append(bytearray(coin.puzzle_hash))
|
||||
for coin in removals:
|
||||
byte_array_tx.append(bytearray(coin.name()))
|
||||
byte_array_tx.append(bytearray(request.coinbase.puzzle_hash))
|
||||
byte_array_tx.append(bytearray(fees_coin.puzzle_hash))
|
||||
|
||||
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||
encoded_filter = bytes(bip158.GetEncoded())
|
||||
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||
encoded_filter = bytes(bip158.GetEncoded())
|
||||
|
||||
proof_of_space_hash: bytes32 = request.proof_of_space.get_hash()
|
||||
body_hash: Body = body.get_hash()
|
||||
difficulty = self.blockchain.get_next_difficulty(target_tip.header_hash)
|
||||
|
||||
assert target_tip_block is not None
|
||||
@ -1309,17 +1297,33 @@ class FullNode:
|
||||
additions_root = addition_merkle_set.get_root()
|
||||
removal_root = removal_merkle_set.get_root()
|
||||
|
||||
generator_hash = (
|
||||
solution_program.get_hash()
|
||||
if solution_program is not None
|
||||
else bytes32([0] * 32)
|
||||
)
|
||||
filter_hash = (
|
||||
std_hash(encoded_filter)
|
||||
if encoded_filter is not None
|
||||
else bytes32([0] * 32)
|
||||
)
|
||||
block_header_data: HeaderData = HeaderData(
|
||||
uint32(target_tip.height + 1),
|
||||
prev_header_hash,
|
||||
timestamp,
|
||||
encoded_filter,
|
||||
filter_hash,
|
||||
proof_of_space_hash,
|
||||
body_hash,
|
||||
target_tip.weight + difficulty,
|
||||
uint64(target_tip.data.total_iters + iterations_needed),
|
||||
additions_root,
|
||||
removal_root,
|
||||
request.coinbase,
|
||||
request.coinbase_signature,
|
||||
fees_coin,
|
||||
aggregate_sig,
|
||||
cost,
|
||||
extension_data,
|
||||
generator_hash,
|
||||
)
|
||||
|
||||
block_header_data_hash: bytes32 = block_header_data.get_hash()
|
||||
@ -1327,7 +1331,8 @@ class FullNode:
|
||||
# Stores this block so we can submit it to the blockchain after it's signed by harvester
|
||||
self.store.add_candidate_block(
|
||||
proof_of_space_hash,
|
||||
body,
|
||||
solution_program,
|
||||
encoded_filter,
|
||||
block_header_data,
|
||||
request.proof_of_space,
|
||||
target_tip.height + 1,
|
||||
@ -1350,7 +1355,7 @@ class FullNode:
|
||||
we call the unfinished_block routine.
|
||||
"""
|
||||
candidate: Optional[
|
||||
Tuple[Body, HeaderData, ProofOfSpace]
|
||||
Tuple[Optional[Program], Optional[bytes], HeaderData, ProofOfSpace]
|
||||
] = self.store.get_candidate_block(header_signature.pos_hash)
|
||||
if candidate is None:
|
||||
self.log.warning(
|
||||
@ -1358,14 +1363,16 @@ class FullNode:
|
||||
)
|
||||
return
|
||||
# Verifies that we have the correct header and body self.stored
|
||||
block_body, block_header_data, pos = candidate
|
||||
generator, filt, block_header_data, pos = candidate
|
||||
|
||||
assert block_header_data.get_hash() == header_signature.header_hash
|
||||
|
||||
block_header: Header = Header(
|
||||
block_header_data, header_signature.header_signature
|
||||
)
|
||||
unfinished_block_obj: FullBlock = FullBlock(pos, None, block_header, block_body)
|
||||
unfinished_block_obj: FullBlock = FullBlock(
|
||||
pos, None, block_header, generator, filt
|
||||
)
|
||||
|
||||
# Propagate to ourselves (which validates and does further propagations)
|
||||
request = full_node_protocol.RespondUnfinishedBlock(unfinished_block_obj)
|
||||
@ -1400,7 +1407,8 @@ class FullNode:
|
||||
unfinished_block_obj.proof_of_space,
|
||||
request.proof,
|
||||
unfinished_block_obj.header,
|
||||
unfinished_block_obj.body,
|
||||
unfinished_block_obj.transactions_generator,
|
||||
unfinished_block_obj.transactions_filter,
|
||||
)
|
||||
|
||||
if self.store.get_sync_mode():
|
||||
|
@ -342,8 +342,8 @@ class MempoolManager:
|
||||
This function removes removals and additions that happened in block from mempool.
|
||||
"""
|
||||
removals, additions = await new_tip.tx_removals_and_additions()
|
||||
additions.append(new_tip.body.coinbase)
|
||||
additions.append(new_tip.body.fees_coin)
|
||||
additions.append(new_tip.header.data.coinbase)
|
||||
additions.append(new_tip.header.data.fees_coin)
|
||||
pool.header = new_tip.header
|
||||
items: Dict[bytes32, MempoolItem] = {}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import aiosqlite
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from src.types.body import Body
|
||||
from src.types.hashable.program import Program
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.header import HeaderData, Header
|
||||
from src.types.header_block import HeaderBlock
|
||||
@ -42,7 +42,10 @@ class FullNodeStore:
|
||||
# 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
|
||||
candidate_blocks: Dict[bytes32, Tuple[Body, HeaderData, ProofOfSpace, uint32]]
|
||||
candidate_blocks: Dict[
|
||||
bytes32,
|
||||
Tuple[Optional[Program], Optional[bytes], HeaderData, ProofOfSpace, uint32],
|
||||
]
|
||||
# Blocks which are not finalized yet (no proof of time), old ones are cleared
|
||||
unfinished_blocks: Dict[Tuple[bytes32, uint64], FullBlock]
|
||||
# Header hashes of unfinished blocks that we have seen recently
|
||||
@ -270,25 +273,32 @@ class FullNodeStore:
|
||||
def add_candidate_block(
|
||||
self,
|
||||
pos_hash: bytes32,
|
||||
body: Body,
|
||||
transactions_generator: Optional[Program],
|
||||
transactions_filter: Optional[bytes],
|
||||
header: HeaderData,
|
||||
pos: ProofOfSpace,
|
||||
height: uint32 = uint32(0),
|
||||
):
|
||||
self.candidate_blocks[pos_hash] = (body, header, pos, height)
|
||||
self.candidate_blocks[pos_hash] = (
|
||||
transactions_generator,
|
||||
transactions_filter,
|
||||
header,
|
||||
pos,
|
||||
height,
|
||||
)
|
||||
|
||||
def get_candidate_block(
|
||||
self, pos_hash: bytes32
|
||||
) -> Optional[Tuple[Body, HeaderData, ProofOfSpace]]:
|
||||
) -> Optional[Tuple[Optional[Program], Optional[bytes], HeaderData, ProofOfSpace]]:
|
||||
res = self.candidate_blocks.get(pos_hash, None)
|
||||
if res is None:
|
||||
return None
|
||||
return (res[0], res[1], res[2])
|
||||
return (res[0], res[1], res[2], res[3])
|
||||
|
||||
def clear_candidate_blocks_below(self, height: uint32) -> None:
|
||||
del_keys = []
|
||||
for key, value in self.candidate_blocks.items():
|
||||
if value[3] < height:
|
||||
if value[4] < height:
|
||||
del_keys.append(key)
|
||||
for key in del_keys:
|
||||
try:
|
||||
|
@ -1,10 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple
|
||||
from typing import List, Tuple, Optional
|
||||
|
||||
from src.types.body import Body
|
||||
from src.types.hashable.coin import Coin
|
||||
from src.types.hashable.spend_bundle import SpendBundle
|
||||
from src.types.header_block import HeaderBlock
|
||||
from src.types.header import Header
|
||||
from src.types.hashable.program import Program
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.cbor_message import cbor_message
|
||||
from src.util.ints import uint32
|
||||
@ -44,16 +45,15 @@ class RequestHeader:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class Header:
|
||||
class RespondHeader:
|
||||
header_block: HeaderBlock
|
||||
bip158_filter: bytes
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RequestAncestors:
|
||||
header_hash: bytes32
|
||||
previous_heights_desired: List[uint32]
|
||||
class RequestAllProofHashes:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -72,7 +72,8 @@ class RequestBody:
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RespondBody:
|
||||
body: Body
|
||||
header: Header
|
||||
transactions_generator: Optional[Program]
|
||||
height: uint32
|
||||
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from src.types.hashable.BLSSignature import BLSSignature
|
||||
from src.types.hashable.program import Program
|
||||
from src.types.hashable.coin import Coin
|
||||
from src.util.ints import uint64
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from src.types.sized_bytes import bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@streamable
|
||||
class Body(Streamable):
|
||||
coinbase: Coin
|
||||
coinbase_signature: BLSSignature
|
||||
fees_coin: Coin
|
||||
transactions: Optional[Program]
|
||||
aggregated_signature: Optional[BLSSignature]
|
||||
cost: uint64
|
||||
extension_data: bytes32
|
@ -2,7 +2,7 @@ from dataclasses import dataclass
|
||||
from typing import Tuple, List, Optional
|
||||
|
||||
from src.types.name_puzzle_condition import NPC
|
||||
from src.types.body import Body
|
||||
from src.types.hashable.program import Program
|
||||
from src.types.hashable.coin import Coin
|
||||
from src.types.header import Header
|
||||
from src.types.sized_bytes import bytes32
|
||||
@ -32,7 +32,8 @@ class FullBlock(Streamable):
|
||||
proof_of_space: ProofOfSpace
|
||||
proof_of_time: Optional[ProofOfTime]
|
||||
header: Header
|
||||
body: Body
|
||||
transactions_generator: Optional[Program]
|
||||
transactions_filter: Optional[bytes]
|
||||
|
||||
@property
|
||||
def prev_header_hash(self) -> bytes32:
|
||||
@ -53,15 +54,17 @@ class FullBlock(Streamable):
|
||||
def additions(self) -> List[Coin]:
|
||||
additions: List[Coin] = []
|
||||
|
||||
if self.body.transactions is not None:
|
||||
if self.transactions_generator is not None:
|
||||
# This should never throw here, block must be valid if it comes to here
|
||||
err, npc_list, cost = get_name_puzzle_conditions(self.body.transactions)
|
||||
err, npc_list, cost = get_name_puzzle_conditions(
|
||||
self.transactions_generator
|
||||
)
|
||||
# created coins
|
||||
if npc_list is not None:
|
||||
additions.extend(additions_for_npc(npc_list))
|
||||
|
||||
additions.append(self.body.coinbase)
|
||||
additions.append(self.body.fees_coin)
|
||||
additions.append(self.header.data.coinbase)
|
||||
additions.append(self.header.data.fees_coin)
|
||||
|
||||
return additions
|
||||
|
||||
@ -74,9 +77,11 @@ class FullBlock(Streamable):
|
||||
removals: List[bytes32] = []
|
||||
additions: List[Coin] = []
|
||||
|
||||
if self.body.transactions is not None:
|
||||
if self.transactions_generator is not None:
|
||||
# This should never throw here, block must be valid if it comes to here
|
||||
err, npc_list, cost = get_name_puzzle_conditions(self.body.transactions)
|
||||
err, npc_list, cost = get_name_puzzle_conditions(
|
||||
self.transactions_generator
|
||||
)
|
||||
# build removals list
|
||||
if npc_list is None:
|
||||
return [], []
|
||||
|
@ -1,10 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import Optional
|
||||
from blspy import PrependSignature
|
||||
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint64, uint32
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from src.types.hashable.BLSSignature import BLSSignature
|
||||
from src.types.hashable.coin import Coin
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -13,13 +16,19 @@ class HeaderData(Streamable):
|
||||
height: uint32
|
||||
prev_header_hash: bytes32
|
||||
timestamp: uint64
|
||||
filter: bytes
|
||||
filter_hash: bytes32
|
||||
proof_of_space_hash: bytes32
|
||||
body_hash: bytes32
|
||||
weight: uint64
|
||||
total_iters: uint64
|
||||
additions_root: bytes32
|
||||
removals_root: bytes32
|
||||
coinbase: Coin
|
||||
coinbase_signature: BLSSignature
|
||||
fees_coin: Coin
|
||||
aggregated_signature: Optional[BLSSignature]
|
||||
cost: uint64
|
||||
extension_data: bytes32
|
||||
generator_hash: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -35,7 +35,7 @@ async def start_ssh_server(ssh_port: int, ssh_key_filename: str, rpc_port: int):
|
||||
uis = [] # type: ignore
|
||||
permenantly_closed = False
|
||||
ssh_server = None
|
||||
node_stop_task: Optional[asyncio.Task] = None
|
||||
node_stop_task = None
|
||||
|
||||
rpc_client: RpcClient = await RpcClient.create(rpc_port)
|
||||
|
||||
|
@ -114,19 +114,19 @@ class WalletNode:
|
||||
|
||||
additions: List[Coin] = []
|
||||
|
||||
if self.wallet.can_generate_puzzle_hash(response.body.coinbase.puzzle_hash):
|
||||
if self.wallet.can_generate_puzzle_hash(response.header.data.coinbase.puzzle_hash):
|
||||
await self.wallet_state_manager.coin_added(
|
||||
response.body.coinbase, response.height, True
|
||||
response.header.data.coinbase, response.height, True
|
||||
)
|
||||
if self.wallet.can_generate_puzzle_hash(response.body.fees_coin.puzzle_hash):
|
||||
if self.wallet.can_generate_puzzle_hash(response.header.data.fees_coin.puzzle_hash):
|
||||
await self.wallet_state_manager.coin_added(
|
||||
response.body.fees_coin, response.height, True
|
||||
response.header.data.fees_coin, response.height, True
|
||||
)
|
||||
|
||||
npc_list: List[NPC]
|
||||
if response.body.transactions:
|
||||
if response.transactions_generator:
|
||||
error, npc_list, cost = get_name_puzzle_conditions(
|
||||
response.body.transactions
|
||||
response.transactions_generator
|
||||
)
|
||||
|
||||
additions.extend(additions_for_npc(npc_list))
|
||||
|
@ -16,7 +16,6 @@ from src.consensus import block_rewards, pot_iterations
|
||||
from src.consensus.constants import constants
|
||||
from src.consensus.pot_iterations import calculate_ips_from_iterations
|
||||
from src.pool import create_coinbase_coin_and_signature
|
||||
from src.types.body import Body
|
||||
from src.types.challenge import Challenge
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
from src.types.full_block import FullBlock, additions_for_npc
|
||||
@ -438,20 +437,11 @@ class BlockTools:
|
||||
fee_hash = blspy.Util.hash256(coinbase_coin.name())
|
||||
fees_coin = Coin(fee_hash, reward_puzzlehash, uint64(fee_reward))
|
||||
|
||||
body: Body = Body(
|
||||
coinbase_coin,
|
||||
coinbase_signature,
|
||||
fees_coin,
|
||||
transactions,
|
||||
aggsig,
|
||||
cost,
|
||||
extension_data,
|
||||
)
|
||||
|
||||
# Create filter
|
||||
byte_array_tx: List[bytes32] = []
|
||||
tx_additions: List[Coin] = []
|
||||
tx_removals: List[bytes32] = []
|
||||
encoded = None
|
||||
if transactions:
|
||||
error, npc_list, _ = get_name_puzzle_conditions(transactions)
|
||||
additions: List[Coin] = additions_for_npc(npc_list)
|
||||
@ -462,11 +452,8 @@ class BlockTools:
|
||||
tx_removals.append(npc.coin_name)
|
||||
byte_array_tx.append(bytearray(npc.coin_name))
|
||||
|
||||
byte_array_tx.append(bytearray(coinbase_coin.puzzle_hash))
|
||||
byte_array_tx.append(bytearray(fees_coin.puzzle_hash))
|
||||
|
||||
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||
encoded = bytes(bip158.GetEncoded())
|
||||
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||
encoded = bytes(bip158.GetEncoded())
|
||||
|
||||
removal_merkle_set = MerkleSet()
|
||||
addition_merkle_set = MerkleSet()
|
||||
@ -494,24 +481,37 @@ class BlockTools:
|
||||
additions_root = addition_merkle_set.get_root()
|
||||
removal_root = removal_merkle_set.get_root()
|
||||
|
||||
generator_hash = (
|
||||
transactions.get_hash() if transactions is not None else bytes32([0] * 32)
|
||||
)
|
||||
filter_hash = std_hash(encoded) if encoded is not None else bytes32([0] * 32)
|
||||
|
||||
header_data: HeaderData = HeaderData(
|
||||
height,
|
||||
prev_header_hash,
|
||||
timestamp,
|
||||
encoded,
|
||||
filter_hash,
|
||||
proof_of_space.get_hash(),
|
||||
body.get_hash(),
|
||||
uint64(prev_weight + difficulty),
|
||||
uint64(prev_iters + number_iters),
|
||||
additions_root,
|
||||
removal_root,
|
||||
coinbase_coin,
|
||||
coinbase_signature,
|
||||
fees_coin,
|
||||
aggsig,
|
||||
cost,
|
||||
extension_data,
|
||||
generator_hash,
|
||||
)
|
||||
|
||||
header_hash_sig: PrependSignature = plot_sk.sign_prepend(header_data.get_hash())
|
||||
|
||||
header: Header = Header(header_data, header_hash_sig)
|
||||
|
||||
full_block: FullBlock = FullBlock(proof_of_space, proof_of_time, header, body)
|
||||
full_block: FullBlock = FullBlock(
|
||||
proof_of_space, proof_of_time, header, transactions, encoded
|
||||
)
|
||||
|
||||
return full_block
|
||||
|
||||
|
@ -8,7 +8,6 @@ from blspy import PrivateKey
|
||||
|
||||
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
|
||||
from src.full_node.store import FullNodeStore
|
||||
from src.types.body import Body
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.hashable.coin import Coin
|
||||
from src.types.header import Header, HeaderData
|
||||
@ -90,17 +89,24 @@ class TestBlockValidation:
|
||||
blocks[9].header.data.height,
|
||||
bytes([1] * 32),
|
||||
blocks[9].header.data.timestamp,
|
||||
blocks[9].header.data.filter,
|
||||
blocks[9].header.data.filter_hash,
|
||||
blocks[9].header.data.proof_of_space_hash,
|
||||
blocks[9].header.data.body_hash,
|
||||
blocks[9].header.data.weight,
|
||||
blocks[9].header.data.total_iters,
|
||||
blocks[9].header.data.additions_root,
|
||||
blocks[9].header.data.removals_root,
|
||||
blocks[9].header.data.coinbase,
|
||||
blocks[9].header.data.coinbase_signature,
|
||||
blocks[9].header.data.fees_coin,
|
||||
blocks[9].header.data.aggregated_signature,
|
||||
blocks[9].header.data.cost,
|
||||
blocks[9].header.data.extension_data,
|
||||
blocks[9].header.data.generator_hash,
|
||||
),
|
||||
blocks[9].header.harvester_signature,
|
||||
),
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert (result) == ReceiveBlockResult.DISCONNECTED_BLOCK
|
||||
@ -117,17 +123,24 @@ class TestBlockValidation:
|
||||
blocks[9].header.data.height,
|
||||
blocks[9].header.data.prev_header_hash,
|
||||
blocks[9].header.data.timestamp - 1000,
|
||||
blocks[9].header.data.filter,
|
||||
blocks[9].header.data.filter_hash,
|
||||
blocks[9].header.data.proof_of_space_hash,
|
||||
blocks[9].header.data.body_hash,
|
||||
blocks[9].header.data.weight,
|
||||
blocks[9].header.data.total_iters,
|
||||
blocks[9].header.data.additions_root,
|
||||
blocks[9].header.data.removals_root,
|
||||
blocks[9].header.data.coinbase,
|
||||
blocks[9].header.data.coinbase_signature,
|
||||
blocks[9].header.data.fees_coin,
|
||||
blocks[9].header.data.aggregated_signature,
|
||||
blocks[9].header.data.cost,
|
||||
blocks[9].header.data.extension_data,
|
||||
blocks[9].header.data.generator_hash,
|
||||
),
|
||||
blocks[9].header.harvester_signature,
|
||||
),
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert (result) == ReceiveBlockResult.INVALID_BLOCK
|
||||
@ -141,23 +154,30 @@ class TestBlockValidation:
|
||||
blocks[9].header.data.height,
|
||||
blocks[9].header.data.prev_header_hash,
|
||||
uint64(int(time.time() + 3600 * 3)),
|
||||
blocks[9].header.data.filter,
|
||||
blocks[9].header.data.filter_hash,
|
||||
blocks[9].header.data.proof_of_space_hash,
|
||||
blocks[9].header.data.body_hash,
|
||||
blocks[9].header.data.weight,
|
||||
blocks[9].header.data.total_iters,
|
||||
blocks[9].header.data.additions_root,
|
||||
blocks[9].header.data.removals_root,
|
||||
blocks[9].header.data.coinbase,
|
||||
blocks[9].header.data.coinbase_signature,
|
||||
blocks[9].header.data.fees_coin,
|
||||
blocks[9].header.data.aggregated_signature,
|
||||
blocks[9].header.data.cost,
|
||||
blocks[9].header.data.extension_data,
|
||||
blocks[9].header.data.generator_hash,
|
||||
),
|
||||
blocks[9].header.harvester_signature,
|
||||
),
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert (result) == ReceiveBlockResult.INVALID_BLOCK
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_body_hash(self, initial_blockchain):
|
||||
async def test_generator_hash(self, initial_blockchain):
|
||||
blocks, b = initial_blockchain
|
||||
block_bad = FullBlock(
|
||||
blocks[9].proof_of_space,
|
||||
@ -167,17 +187,24 @@ class TestBlockValidation:
|
||||
blocks[9].header.data.height,
|
||||
blocks[9].header.data.prev_header_hash,
|
||||
blocks[9].header.data.timestamp,
|
||||
blocks[9].header.data.filter,
|
||||
blocks[9].header.data.filter_hash,
|
||||
blocks[9].header.data.proof_of_space_hash,
|
||||
bytes([1] * 32),
|
||||
blocks[9].header.data.weight,
|
||||
blocks[9].header.data.total_iters,
|
||||
blocks[9].header.data.additions_root,
|
||||
blocks[9].header.data.removals_root,
|
||||
blocks[9].header.data.coinbase,
|
||||
blocks[9].header.data.coinbase_signature,
|
||||
blocks[9].header.data.fees_coin,
|
||||
blocks[9].header.data.aggregated_signature,
|
||||
blocks[9].header.data.cost,
|
||||
blocks[9].header.data.extension_data,
|
||||
bytes([1] * 32),
|
||||
),
|
||||
blocks[9].header.harvester_signature,
|
||||
),
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert result == ReceiveBlockResult.INVALID_BLOCK
|
||||
@ -193,7 +220,8 @@ class TestBlockValidation:
|
||||
blocks[9].header.data,
|
||||
PrivateKey.from_seed(b"0").sign_prepend(b"random junk"),
|
||||
),
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert result == ReceiveBlockResult.INVALID_BLOCK
|
||||
@ -215,7 +243,8 @@ class TestBlockValidation:
|
||||
),
|
||||
blocks[9].proof_of_time,
|
||||
blocks[9].header,
|
||||
blocks[9].body,
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert result == ReceiveBlockResult.INVALID_BLOCK
|
||||
@ -228,20 +257,33 @@ class TestBlockValidation:
|
||||
block_bad = FullBlock(
|
||||
blocks[9].proof_of_space,
|
||||
blocks[9].proof_of_time,
|
||||
blocks[9].header,
|
||||
Body(
|
||||
Coin(
|
||||
blocks[7].body.coinbase.parent_coin_info,
|
||||
blocks[9].body.coinbase.puzzle_hash,
|
||||
uint64(9999999999),
|
||||
Header(
|
||||
HeaderData(
|
||||
blocks[9].header.data.height,
|
||||
blocks[9].header.data.prev_header_hash,
|
||||
blocks[9].header.data.timestamp,
|
||||
blocks[9].header.data.filter_hash,
|
||||
blocks[9].header.data.proof_of_space_hash,
|
||||
blocks[9].header.data.weight,
|
||||
blocks[9].header.data.total_iters,
|
||||
blocks[9].header.data.additions_root,
|
||||
blocks[9].header.data.removals_root,
|
||||
Coin(
|
||||
blocks[9].header.data.coinbase.parent_coin_info,
|
||||
blocks[9].header.data.coinbase.puzzle_hash,
|
||||
uint64(9999999999),
|
||||
),
|
||||
blocks[9].header.data.coinbase_signature,
|
||||
blocks[9].header.data.fees_coin,
|
||||
blocks[9].header.data.aggregated_signature,
|
||||
blocks[9].header.data.cost,
|
||||
blocks[9].header.data.extension_data,
|
||||
blocks[9].header.data.generator_hash,
|
||||
),
|
||||
blocks[9].body.coinbase_signature,
|
||||
blocks[9].body.fees_coin,
|
||||
None,
|
||||
blocks[9].body.aggregated_signature,
|
||||
blocks[9].body.cost,
|
||||
blocks[9].body.extension_data,
|
||||
blocks[9].header.harvester_signature,
|
||||
),
|
||||
blocks[9].transactions_generator,
|
||||
blocks[9].transactions_filter,
|
||||
)
|
||||
result, removed = await b.receive_block(block_bad)
|
||||
assert result == ReceiveBlockResult.INVALID_BLOCK
|
||||
@ -324,7 +366,6 @@ class TestReorgs:
|
||||
test_constants, 21, [blocks[0]], 9, b"3"
|
||||
)
|
||||
for i in range(1, len(blocks_reorg_chain)):
|
||||
print("I", i)
|
||||
reorg_block = blocks_reorg_chain[i]
|
||||
result, removed = await b.receive_block(reorg_block)
|
||||
if reorg_block.height == 0:
|
||||
|
@ -56,7 +56,7 @@ class TestBlockchainTransactions:
|
||||
spent_block = blocks[1]
|
||||
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spent_block.body.coinbase
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
||||
)
|
||||
|
||||
assert spend_bundle is not None
|
||||
@ -115,8 +115,8 @@ class TestBlockchainTransactions:
|
||||
|
||||
assert in_full_tips
|
||||
assert farmed_block is not None
|
||||
assert farmed_block.body.transactions == program
|
||||
assert farmed_block.body.aggregated_signature == aggsig
|
||||
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):
|
||||
@ -141,10 +141,10 @@ class TestBlockchainTransactions:
|
||||
spent_block = blocks[1]
|
||||
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spent_block.body.coinbase
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
||||
)
|
||||
spend_bundle_double = wallet_a.generate_signed_transaction(
|
||||
1001, receiver_puzzlehash, spent_block.body.coinbase
|
||||
1001, receiver_puzzlehash, spent_block.header.data.coinbase
|
||||
)
|
||||
|
||||
block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double])
|
||||
@ -158,7 +158,7 @@ class TestBlockchainTransactions:
|
||||
|
||||
next_block = new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.DOUBLE_SPEND
|
||||
@ -186,10 +186,10 @@ class TestBlockchainTransactions:
|
||||
spent_block = blocks[1]
|
||||
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spent_block.body.coinbase
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
||||
)
|
||||
spend_bundle_double = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spent_block.body.coinbase
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase
|
||||
)
|
||||
|
||||
block_spendbundle = SpendBundle.aggregate([spend_bundle, spend_bundle_double])
|
||||
@ -203,7 +203,7 @@ class TestBlockchainTransactions:
|
||||
|
||||
next_block = new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.DUPLICATE_OUTPUT
|
||||
@ -233,20 +233,24 @@ class TestBlockchainTransactions:
|
||||
spent_block = blocks[1]
|
||||
bad_block = blocks[2]
|
||||
valid_cvp = ConditionVarPair(
|
||||
ConditionOpcode.ASSERT_MY_COIN_ID, spent_block.body.coinbase.name(), None
|
||||
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.body.coinbase.name(), None
|
||||
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.body.coinbase, bad_dic
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase, bad_dic
|
||||
)
|
||||
|
||||
valid_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spent_block.body.coinbase, valid_dic
|
||||
1000, receiver_puzzlehash, spent_block.header.data.coinbase, valid_dic
|
||||
)
|
||||
|
||||
# Invalid block bundle
|
||||
@ -263,7 +267,7 @@ class TestBlockchainTransactions:
|
||||
# Try to validate that block
|
||||
next_block = invalid_new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.ASSERT_MY_COIN_ID_FAILED
|
||||
@ -280,7 +284,7 @@ class TestBlockchainTransactions:
|
||||
)
|
||||
next_block = new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
assert error is None
|
||||
|
||||
@ -311,20 +315,24 @@ class TestBlockchainTransactions:
|
||||
|
||||
# This condition requires block2 coinbase to be spent
|
||||
block1_cvp = ConditionVarPair(
|
||||
ConditionOpcode.ASSERT_COIN_CONSUMED, block2.body.coinbase.name(), None
|
||||
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.body.coinbase, block1_dic
|
||||
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.body.coinbase.name(), None
|
||||
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.body.coinbase, block2_dic
|
||||
1000, receiver_puzzlehash, block2.header.data.coinbase, block2_dic
|
||||
)
|
||||
|
||||
# Invalid block bundle
|
||||
@ -341,7 +349,7 @@ class TestBlockchainTransactions:
|
||||
# Try to validate that block
|
||||
next_block = invalid_new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.ASSERT_COIN_CONSUMED_FAILED
|
||||
@ -362,7 +370,7 @@ class TestBlockchainTransactions:
|
||||
# Try to validate newly created block
|
||||
next_block = new_blocks[11]
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is None
|
||||
@ -397,7 +405,7 @@ class TestBlockchainTransactions:
|
||||
)
|
||||
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
||||
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block1.body.coinbase, block1_dic
|
||||
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
||||
)
|
||||
|
||||
# program that will be sent to early
|
||||
@ -414,7 +422,7 @@ class TestBlockchainTransactions:
|
||||
# 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.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.ASSERT_BLOCK_INDEX_EXCEEDS_FAILED
|
||||
@ -434,7 +442,7 @@ class TestBlockchainTransactions:
|
||||
next_block = valid_new_blocks[12]
|
||||
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is None
|
||||
@ -470,7 +478,7 @@ class TestBlockchainTransactions:
|
||||
)
|
||||
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
||||
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block1.body.coinbase, block1_dic
|
||||
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
||||
)
|
||||
|
||||
# program that will be sent to early
|
||||
@ -487,7 +495,7 @@ class TestBlockchainTransactions:
|
||||
# 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.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.ASSERT_BLOCK_AGE_EXCEEDS_FAILED
|
||||
@ -507,7 +515,7 @@ class TestBlockchainTransactions:
|
||||
next_block = valid_new_blocks[12]
|
||||
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is None
|
||||
@ -543,7 +551,7 @@ class TestBlockchainTransactions:
|
||||
)
|
||||
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
||||
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block1.body.coinbase, block1_dic
|
||||
1000, receiver_puzzlehash, block1.header.data.coinbase, block1_dic
|
||||
)
|
||||
|
||||
# program that will be sent to early
|
||||
@ -560,7 +568,7 @@ class TestBlockchainTransactions:
|
||||
# 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.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is Err.ASSERT_TIME_EXCEEDS_FAILED
|
||||
@ -583,7 +591,7 @@ class TestBlockchainTransactions:
|
||||
next_block = valid_new_blocks[12]
|
||||
|
||||
error = await full_node_1.blockchain._validate_transactions(
|
||||
next_block, next_block.body.fees_coin.amount
|
||||
next_block, next_block.header.data.fees_coin.amount
|
||||
)
|
||||
|
||||
assert error is None
|
||||
|
@ -100,7 +100,7 @@ class TestFullNode:
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
100,
|
||||
receiver_puzzlehash,
|
||||
blocks[1].body.coinbase,
|
||||
blocks[1].header.data.coinbase,
|
||||
condition_dic=conditions_dict,
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
@ -197,7 +197,7 @@ class TestFullNode:
|
||||
|
||||
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
100, receiver_puzzlehash, blocks[2].body.coinbase,
|
||||
100, receiver_puzzlehash, blocks[2].header.data.coinbase,
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
respond_transaction = fnp.RespondTransaction(spend_bundle)
|
||||
@ -225,7 +225,7 @@ class TestFullNode:
|
||||
|
||||
# Invalid transaction does not propagate
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
100000000000000, receiver_puzzlehash, blocks[3].body.coinbase,
|
||||
100000000000000, receiver_puzzlehash, blocks[3].header.data.coinbase,
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
respond_transaction = fnp.RespondTransaction(spend_bundle)
|
||||
@ -255,7 +255,8 @@ class TestFullNode:
|
||||
blocks_new[-1].proof_of_space,
|
||||
None,
|
||||
blocks_new[-1].header,
|
||||
blocks_new[-1].body,
|
||||
blocks_new[-1].transactions_generator,
|
||||
blocks_new[-1].transactions_filter,
|
||||
)
|
||||
unf_block_req = fnp.RespondUnfinishedBlock(unf_block)
|
||||
|
||||
@ -336,7 +337,8 @@ class TestFullNode:
|
||||
blocks_new[-1].proof_of_space,
|
||||
None,
|
||||
blocks_new[-1].header,
|
||||
blocks_new[-1].body,
|
||||
blocks_new[-1].transactions_generator,
|
||||
blocks_new[-1].transactions_filter,
|
||||
)
|
||||
unf_block_req = fnp.RespondUnfinishedBlock(unf_block)
|
||||
[x async for x in full_node_1.respond_unfinished_block(unf_block_req)]
|
||||
@ -395,7 +397,8 @@ class TestFullNode:
|
||||
blocks_new[-1].proof_of_space,
|
||||
None,
|
||||
blocks_new[-1].header,
|
||||
blocks_new[-1].body,
|
||||
blocks_new[-1].transactions_generator,
|
||||
blocks_new[-1].transactions_filter,
|
||||
)
|
||||
unf_block_req = fnp.RespondUnfinishedBlock(unf_block)
|
||||
[x async for x in full_node_1.respond_unfinished_block(unf_block_req)]
|
||||
@ -425,7 +428,8 @@ class TestFullNode:
|
||||
blocks_new[-1].proof_of_space,
|
||||
None,
|
||||
blocks_new[-1].header,
|
||||
blocks_new[-1].body,
|
||||
blocks_new[-1].transactions_generator,
|
||||
blocks_new[-1].transactions_filter,
|
||||
)
|
||||
unf_block_req = fnp.RespondUnfinishedBlock(unf_block)
|
||||
|
||||
@ -487,7 +491,8 @@ class TestFullNode:
|
||||
blocks_new[30].proof_of_space,
|
||||
None,
|
||||
blocks_new[30].header,
|
||||
blocks_new[30].body,
|
||||
blocks_new[30].transactions_generator,
|
||||
blocks_new[30].transactions_filter,
|
||||
)
|
||||
|
||||
unf_block_req_bad = fnp.RespondUnfinishedBlock(unf_block_not_child)
|
||||
@ -510,7 +515,8 @@ class TestFullNode:
|
||||
candidates[index].proof_of_space,
|
||||
None,
|
||||
candidates[index].header,
|
||||
candidates[index].body,
|
||||
candidates[index].transactions_generator,
|
||||
candidates[index].transactions_filter,
|
||||
)
|
||||
return fnp.RespondUnfinishedBlock(unf_block)
|
||||
|
||||
@ -587,7 +593,8 @@ class TestFullNode:
|
||||
blocks_new_3[-1].proof_of_space,
|
||||
None,
|
||||
blocks_new_3[-1].header,
|
||||
blocks_new_3[-1].body,
|
||||
blocks_new_3[-1].transactions_generator,
|
||||
blocks_new_3[-1].transactions_filter,
|
||||
)
|
||||
|
||||
unf_block_new_req = fnp.RespondUnfinishedBlock(unf_block_new)
|
||||
@ -705,7 +712,8 @@ class TestFullNode:
|
||||
),
|
||||
blocks_new[-5].proof_of_time,
|
||||
blocks_new[-5].header,
|
||||
blocks_new[-5].body,
|
||||
blocks_new[-5].transactions_generator,
|
||||
blocks_new[-5].transactions_filter,
|
||||
)
|
||||
msgs = [
|
||||
_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block_invalid))
|
||||
|
@ -49,7 +49,7 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
||||
@ -83,7 +83,7 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
||||
@ -135,7 +135,7 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -149,7 +149,7 @@ class TestMempool:
|
||||
|
||||
other_receiver = WalletTool()
|
||||
spend_bundle2 = wallet_a.generate_signed_transaction(
|
||||
1000, other_receiver.get_new_puzzlehash(), block.body.coinbase
|
||||
1000, other_receiver.get_new_puzzlehash(), block.header.data.coinbase
|
||||
)
|
||||
assert spend_bundle2 is not None
|
||||
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
||||
@ -184,7 +184,7 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase
|
||||
)
|
||||
assert spend_bundle1 is not None
|
||||
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
|
||||
@ -196,7 +196,7 @@ class TestMempool:
|
||||
assert outbound.message.function == "new_transaction"
|
||||
|
||||
spend_bundle2 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, fee=1
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, fee=1
|
||||
)
|
||||
|
||||
assert spend_bundle2 is not None
|
||||
@ -239,7 +239,7 @@ class TestMempool:
|
||||
dic = {ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -282,7 +282,7 @@ class TestMempool:
|
||||
dic = {ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -323,7 +323,7 @@ class TestMempool:
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -366,7 +366,7 @@ class TestMempool:
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -404,12 +404,12 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
cvp = ConditionVarPair(
|
||||
ConditionOpcode.ASSERT_MY_COIN_ID, block.body.coinbase.name(), None
|
||||
ConditionOpcode.ASSERT_MY_COIN_ID, block.header.data.coinbase.name(), None
|
||||
)
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -447,12 +447,14 @@ class TestMempool:
|
||||
pass
|
||||
|
||||
cvp = ConditionVarPair(
|
||||
ConditionOpcode.ASSERT_MY_COIN_ID, blocks[2].body.coinbase.name(), None
|
||||
ConditionOpcode.ASSERT_MY_COIN_ID,
|
||||
blocks[2].header.data.coinbase.name(),
|
||||
None,
|
||||
)
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -497,7 +499,7 @@ class TestMempool:
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
@ -545,7 +547,7 @@ class TestMempool:
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, block.body.coinbase, dic
|
||||
1000, receiver_puzzlehash, block.header.data.coinbase, dic
|
||||
)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
|
@ -89,7 +89,8 @@ class TestStore:
|
||||
# Add/get candidate block
|
||||
assert db.get_candidate_block(0) is None
|
||||
partial = (
|
||||
blocks[5].body,
|
||||
blocks[5].transactions_generator,
|
||||
blocks[5].transactions_filter,
|
||||
blocks[5].header.data,
|
||||
blocks[5].proof_of_space,
|
||||
)
|
||||
|
@ -43,10 +43,10 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
assert block.body.coinbase == unspent.coin
|
||||
assert block.body.fees_coin == unspent_fee.coin
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
assert block.header.data.coinbase == unspent.coin
|
||||
assert block.header.data.fees_coin == unspent_fee.coin
|
||||
|
||||
await db.close()
|
||||
Path("fndb_test.db").unlink()
|
||||
@ -61,15 +61,15 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
assert block.body.coinbase == unspent.coin
|
||||
assert block.body.fees_coin == unspent_fee.coin
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
assert block.header.data.coinbase == unspent.coin
|
||||
assert block.header.data.fees_coin == unspent_fee.coin
|
||||
|
||||
await db.set_spent(unspent.coin.name(), block.height)
|
||||
await db.set_spent(unspent_fee.coin.name(), block.height)
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
|
||||
@ -86,15 +86,15 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
assert block.body.coinbase == unspent.coin
|
||||
assert block.body.fees_coin == unspent_fee.coin
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
assert block.header.data.coinbase == unspent.coin
|
||||
assert block.header.data.fees_coin == unspent_fee.coin
|
||||
|
||||
await db.set_spent(unspent.coin.name(), block.height)
|
||||
await db.set_spent(unspent_fee.coin.name(), block.height)
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
|
||||
@ -102,8 +102,8 @@ class TestUnspent:
|
||||
await db.rollback_lca_to_block(reorg_index)
|
||||
|
||||
for c, block in enumerate(blocks):
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.header.data.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.header.data.fees_coin.name())
|
||||
if c <= reorg_index:
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
@ -129,17 +129,17 @@ class TestUnspent:
|
||||
|
||||
for c, block in enumerate(blocks):
|
||||
unspent = await unspent_store.get_coin_record(
|
||||
block.body.coinbase.name(), block.header
|
||||
block.header.data.coinbase.name(), block.header
|
||||
)
|
||||
unspent_fee = await unspent_store.get_coin_record(
|
||||
block.body.fees_coin.name(), block.header
|
||||
block.header.data.fees_coin.name(), block.header
|
||||
)
|
||||
assert unspent.spent == 0
|
||||
assert unspent_fee.spent == 0
|
||||
assert unspent.confirmed_block_index == block.height
|
||||
assert unspent.spent_block_index == 0
|
||||
assert unspent.name == block.body.coinbase.name()
|
||||
assert unspent_fee.name == block.body.fees_coin.name()
|
||||
assert unspent.name == block.header.data.coinbase.name()
|
||||
assert unspent_fee.name == block.header.data.fees_coin.name()
|
||||
|
||||
blocks_reorg_chain = bt.get_consecutive_blocks(
|
||||
test_constants, 30, blocks[:90], 9, b"1"
|
||||
@ -155,9 +155,9 @@ class TestUnspent:
|
||||
elif reorg_block.height >= 100:
|
||||
assert result == ReceiveBlockResult.ADDED_TO_HEAD
|
||||
unspent = await unspent_store.get_coin_record(
|
||||
reorg_block.body.coinbase.name(), reorg_block.header
|
||||
reorg_block.header.data.coinbase.name(), reorg_block.header
|
||||
)
|
||||
assert unspent.name == reorg_block.body.coinbase.name()
|
||||
assert unspent.name == reorg_block.header.data.coinbase.name()
|
||||
assert unspent.confirmed_block_index == reorg_block.height
|
||||
assert unspent.spent == 0
|
||||
assert unspent.spent_block_index == 0
|
||||
@ -185,7 +185,7 @@ class TestUnspent:
|
||||
await b.receive_block(blocks[i])
|
||||
assert b.get_current_tips()[0].height == num_blocks
|
||||
unspent = await unspent_store.get_coin_record(
|
||||
blocks[1].body.coinbase.name(), blocks[-1].header
|
||||
blocks[1].header.data.coinbase.name(), blocks[-1].header
|
||||
)
|
||||
unspent_puzzle_hash = unspent.coin.puzzle_hash
|
||||
|
||||
|
@ -56,11 +56,11 @@ class TestRpc:
|
||||
assert header == blocks[7].header
|
||||
|
||||
coins = await client.get_unspent_coins(
|
||||
blocks[-1].body.coinbase.puzzle_hash, blocks[-1].header_hash
|
||||
blocks[-1].header.data.coinbase.puzzle_hash, blocks[-1].header_hash
|
||||
)
|
||||
assert len(coins) == 16
|
||||
coins_lca = await client.get_unspent_coins(
|
||||
blocks[-1].body.coinbase.puzzle_hash
|
||||
blocks[-1].header.data.coinbase.puzzle_hash
|
||||
)
|
||||
assert len(coins_lca) == 16
|
||||
|
||||
|
@ -31,9 +31,9 @@ class TestCostCalculation:
|
||||
)
|
||||
|
||||
spend_bundle = wallet_tool.generate_signed_transaction(
|
||||
blocks[1].body.coinbase.amount,
|
||||
blocks[1].header.data.coinbase.amount,
|
||||
receiver.get_new_puzzlehash(),
|
||||
blocks[1].body.coinbase,
|
||||
blocks[1].header.data.coinbase,
|
||||
)
|
||||
assert spend_bundle is not None
|
||||
program = best_solution_program(spend_bundle)
|
||||
|
@ -41,8 +41,8 @@ class TestFilter:
|
||||
for i in range(1, num_blocks):
|
||||
byte_array_tx: List[bytes] = []
|
||||
block = blocks[i]
|
||||
coinbase = bytearray(block.body.coinbase.puzzle_hash)
|
||||
fee = bytearray(block.body.fees_coin.puzzle_hash)
|
||||
coinbase = bytearray(block.header.data.coinbase.puzzle_hash)
|
||||
fee = bytearray(block.header.data.fees_coin.puzzle_hash)
|
||||
byte_array_tx.append(coinbase)
|
||||
byte_array_tx.append(fee)
|
||||
|
||||
|
@ -31,25 +31,25 @@ class TestMerkleSet:
|
||||
merkle_set_reverse = MerkleSet()
|
||||
|
||||
for block in reversed(blocks):
|
||||
merkle_set_reverse.add_already_hashed(block.body.coinbase.name())
|
||||
merkle_set_reverse.add_already_hashed(block.header.data.coinbase.name())
|
||||
|
||||
for block in blocks:
|
||||
merkle_set.add_already_hashed(block.body.coinbase.name())
|
||||
merkle_set.add_already_hashed(block.header.data.coinbase.name())
|
||||
|
||||
for block in blocks:
|
||||
result, proof = merkle_set.is_included_already_hashed(
|
||||
block.body.coinbase.name()
|
||||
block.header.data.coinbase.name()
|
||||
)
|
||||
assert result is True
|
||||
result_fee, proof_fee = merkle_set.is_included_already_hashed(
|
||||
block.body.fees_coin.name()
|
||||
block.header.data.fees_coin.name()
|
||||
)
|
||||
assert result_fee is False
|
||||
validate_proof = confirm_included_already_hashed(
|
||||
merkle_set.get_root(), block.body.coinbase.name(), proof
|
||||
merkle_set.get_root(), block.header.data.coinbase.name(), proof
|
||||
)
|
||||
validate_proof_fee = confirm_included_already_hashed(
|
||||
merkle_set.get_root(), block.body.fees_coin.name(), proof_fee
|
||||
merkle_set.get_root(), block.header.data.fees_coin.name(), proof_fee
|
||||
)
|
||||
assert validate_proof is True
|
||||
assert validate_proof_fee is False
|
||||
|
@ -4,7 +4,6 @@ import pytest
|
||||
from blspy import ExtendedPrivateKey
|
||||
|
||||
from src.protocols.wallet_protocol import RespondBody
|
||||
from src.wallet.wallet import Wallet
|
||||
from src.wallet.wallet_node import WalletNode
|
||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||
|
||||
@ -41,7 +40,9 @@ class TestWallet:
|
||||
)
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
a = RespondBody(blocks[i].body, blocks[i].height)
|
||||
a = RespondBody(
|
||||
blocks[i].header, blocks[i].transactions_generator, blocks[i].height
|
||||
)
|
||||
await wallet_node.received_body(a)
|
||||
|
||||
assert await wallet.get_confirmed_balance() == 144000000000000
|
||||
@ -76,7 +77,9 @@ class TestWallet:
|
||||
)
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
a = RespondBody(blocks[i].body, blocks[i].height)
|
||||
a = RespondBody(
|
||||
blocks[i].header, blocks[i].transactions_generator, blocks[i].height
|
||||
)
|
||||
await wallet_node.received_body(a)
|
||||
|
||||
assert await wallet.get_confirmed_balance() == 144000000000000
|
||||
|
Loading…
Reference in New Issue
Block a user