mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-11-29 13:28:11 +03:00
Merge branch 'integration' of github.com:Chia-Network/chia-blockchain into new-protocol
This commit is contained in:
commit
a04282a916
@ -1,9 +1,10 @@
|
||||
import argparse
|
||||
from hashlib import sha256
|
||||
from secrets import token_bytes
|
||||
|
||||
from blspy import PrivateKey
|
||||
from yaml import safe_dump, safe_load
|
||||
from src.pool import create_puzzlehash_for_pk
|
||||
from src.types.hashable.BLSSignature import BLSPublicKey
|
||||
|
||||
from definitions import ROOT_DIR
|
||||
|
||||
@ -76,7 +77,9 @@ def main():
|
||||
# Replaces the farmer's private key. The farmer target allows spending
|
||||
# of the fees.
|
||||
farmer_sk = PrivateKey.from_seed(token_bytes(32))
|
||||
farmer_target = sha256(bytes(farmer_sk.get_public_key())).digest()
|
||||
farmer_target = create_puzzlehash_for_pk(
|
||||
BLSPublicKey(bytes(farmer_sk.get_public_key()))
|
||||
)
|
||||
key_config["farmer_sk"] = bytes(farmer_sk).hex()
|
||||
key_config["farmer_target"] = farmer_target.hex()
|
||||
with open(key_config_filename, "w") as f:
|
||||
@ -91,7 +94,9 @@ def main():
|
||||
# Replaces the pools keys and targes. Only useful if running a pool, or doing
|
||||
# solo farming. The pool target allows spending of the coinbase.
|
||||
pool_sks = [PrivateKey.from_seed(token_bytes(32)) for _ in range(2)]
|
||||
pool_target = sha256(bytes(pool_sks[0].get_public_key())).digest()
|
||||
pool_target = create_puzzlehash_for_pk(
|
||||
BLSPublicKey(bytes(pool_sks[0].get_public_key()))
|
||||
)
|
||||
key_config["pool_sks"] = [bytes(pool_sk).hex() for pool_sk in pool_sks]
|
||||
key_config["pool_target"] = pool_target.hex()
|
||||
with open(key_config_filename, "w") as f:
|
||||
|
@ -8,18 +8,17 @@ import asyncio
|
||||
import concurrent
|
||||
import blspy
|
||||
|
||||
from src.consensus.block_rewards import calculate_block_reward
|
||||
from src.consensus.block_rewards import calculate_block_reward, calculate_base_fee
|
||||
from src.consensus.constants import constants as consensus_constants
|
||||
from src.consensus.pot_iterations import (
|
||||
calculate_ips_from_iterations,
|
||||
calculate_iterations_quality,
|
||||
)
|
||||
from src.mempool import MAX_COIN_AMOUNT
|
||||
from src.store import FullNodeStore
|
||||
|
||||
from src.types.full_block import FullBlock, additions_for_npc
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.Unspent import Unspent
|
||||
from src.types.hashable.CoinRecord import CoinRecord
|
||||
from src.types.header_block import HeaderBlock, SmallHeaderBlock
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.unspent_store import UnspentStore
|
||||
@ -69,7 +68,7 @@ class Blockchain:
|
||||
# Store
|
||||
store: FullNodeStore
|
||||
# Coinbase freeze period
|
||||
coinbase_freeze: int
|
||||
coinbase_freeze: uint32
|
||||
|
||||
@staticmethod
|
||||
async def create(
|
||||
@ -717,9 +716,9 @@ class Blockchain:
|
||||
return False
|
||||
|
||||
coinbase_reward = calculate_block_reward(block.height)
|
||||
if (coinbase_reward / 8) * 7 != block.body.coinbase.amount:
|
||||
if coinbase_reward != block.body.coinbase.amount:
|
||||
return False
|
||||
fee_base = uint64(int(coinbase_reward / 8))
|
||||
fee_base = calculate_base_fee(block.height)
|
||||
# 8. If there is no agg signature, there should be no transactions either
|
||||
# target reward_fee = 1/8 coinbase reward + tx fees
|
||||
if not block.body.aggregated_signature:
|
||||
@ -1000,7 +999,7 @@ class Blockchain:
|
||||
async def _from_fork_to_lca(
|
||||
self, fork_point: SmallHeaderBlock, lca: SmallHeaderBlock
|
||||
):
|
||||
""" Returns the list of full blocks from fork_point to lca. """
|
||||
""" Selects blocks between fork_point and LCA, and then adds them to unspent_store. """
|
||||
blocks: List[FullBlock] = []
|
||||
tip_hash: bytes32 = lca.header_hash
|
||||
while True:
|
||||
@ -1066,7 +1065,7 @@ class Blockchain:
|
||||
# Check additions for max coin amount
|
||||
for coin in additions:
|
||||
additions_dic[coin.name()] = coin
|
||||
if coin.amount >= MAX_COIN_AMOUNT:
|
||||
if coin.amount >= consensus_constants["MAX_COIN_AMOUNT"]:
|
||||
return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
||||
|
||||
# Watch out for duplicate outputs
|
||||
@ -1082,16 +1081,16 @@ class Blockchain:
|
||||
return Err.DOUBLE_SPEND
|
||||
|
||||
# Check if removals exist and were not previously spend. (unspent_db + diff_store + this_block)
|
||||
removal_unspents: Dict[bytes32, Unspent] = {}
|
||||
removal_coin_records: Dict[bytes32, CoinRecord] = {}
|
||||
for rem in removals:
|
||||
if rem in additions_dic:
|
||||
# Ephemeral coin
|
||||
rem_coin: Coin = additions_dic[rem]
|
||||
new_unspent: Unspent = Unspent(rem_coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
removal_unspents[new_unspent.name] = new_unspent
|
||||
new_unspent: CoinRecord = CoinRecord(rem_coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
removal_coin_records[new_unspent.name] = new_unspent
|
||||
else:
|
||||
assert prev_header is not None
|
||||
unspent = await self.unspent_store.get_unspent(rem, prev_header)
|
||||
unspent = await self.unspent_store.get_coin_record(rem, prev_header)
|
||||
if unspent:
|
||||
if unspent.spent == 1:
|
||||
return Err.DOUBLE_SPEND
|
||||
@ -1101,13 +1100,13 @@ class Blockchain:
|
||||
< unspent.confirmed_block_index + self.coinbase_freeze
|
||||
):
|
||||
return Err.COINBASE_NOT_YET_SPENDABLE
|
||||
removal_unspents[unspent.name] = unspent
|
||||
removal_coin_records[unspent.name] = unspent
|
||||
else:
|
||||
return Err.UNKNOWN_UNSPENT
|
||||
|
||||
# Check fees
|
||||
removed = 0
|
||||
for unspent in removal_unspents.values():
|
||||
for unspent in removal_coin_records.values():
|
||||
removed += unspent.coin.amount
|
||||
|
||||
added = 0
|
||||
@ -1124,17 +1123,17 @@ class Blockchain:
|
||||
return Err.BAD_COINBASE_REWARD
|
||||
|
||||
# Verify that removed coin puzzle_hashes match with calculated puzzle_hashes
|
||||
for unspent in removal_unspents.values():
|
||||
for unspent in removal_coin_records.values():
|
||||
if unspent.coin.puzzle_hash != removals_puzzle_dic[unspent.name]:
|
||||
return Err.WRONG_PUZZLE_HASH
|
||||
|
||||
# Verify conditions, create hash_key list for aggsig check
|
||||
hash_key_pairs = []
|
||||
for npc in npc_list:
|
||||
unspent = removal_unspents[npc.coin_name]
|
||||
unspent = removal_coin_records[npc.coin_name]
|
||||
error = blockchain_check_conditions_dict(
|
||||
unspent,
|
||||
removal_unspents,
|
||||
removal_coin_records,
|
||||
npc.condition_dict,
|
||||
block.header_block.to_small(),
|
||||
)
|
||||
|
@ -6,4 +6,12 @@ def calculate_block_reward(height: uint32) -> uint64:
|
||||
Returns the coinbase reward at a certain block height.
|
||||
1 Chia coin = 16,000,000,000,000 = 16 trillion mojo.
|
||||
"""
|
||||
return uint64(16000000000000)
|
||||
return uint64(14000000000000)
|
||||
|
||||
|
||||
def calculate_base_fee(height: uint32) -> uint64:
|
||||
"""
|
||||
Returns the base fee reward at a certain block height.
|
||||
1 base fee reward is 1/8 of total block reward
|
||||
"""
|
||||
return uint64(2000000000000)
|
||||
|
@ -32,4 +32,6 @@ constants: Dict[str, Any] = {
|
||||
"MEMPOOL_BLOCK_BUFFER": 10,
|
||||
# Coinbase rewards are not spendable for 200 blocks
|
||||
"COINBASE_FREEZE_PERIOD": 200,
|
||||
# Max coin amount int(1 << 48)
|
||||
"MAX_COIN_AMOUNT": 281474976710656,
|
||||
}
|
||||
|
@ -279,8 +279,9 @@ class Farmer:
|
||||
]
|
||||
|
||||
coinbase_reward = uint64(
|
||||
int((calculate_block_reward(proof_of_space_finalized.height) / 8) * 7)
|
||||
calculate_block_reward(proof_of_space_finalized.height)
|
||||
)
|
||||
|
||||
coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature(
|
||||
proof_of_space_finalized.height + 1,
|
||||
bytes.fromhex(self.key_config["pool_target"]),
|
||||
|
@ -9,7 +9,7 @@ from typing import AsyncGenerator, List, Optional, Tuple, Dict
|
||||
|
||||
from chiapos import Verifier
|
||||
from src.blockchain import Blockchain, ReceiveBlockResult
|
||||
from src.consensus.block_rewards import calculate_block_reward
|
||||
from src.consensus.block_rewards import calculate_base_fee
|
||||
from src.consensus.constants import constants
|
||||
from src.consensus.pot_iterations import calculate_iterations
|
||||
from src.consensus.weight_verifier import verify_weight
|
||||
@ -762,8 +762,7 @@ class FullNode:
|
||||
aggregate_sig = spend_bundle.aggregated_signature
|
||||
|
||||
transactions_generator: bytes32 = sha256(b"").digest()
|
||||
full_coinbase_reward = calculate_block_reward(target_tip.height)
|
||||
base_fee_reward = full_coinbase_reward / 8
|
||||
base_fee_reward = calculate_base_fee(target_tip.height)
|
||||
full_fee_reward = uint64(int(base_fee_reward + spend_bundle_fees))
|
||||
# Create fees coin
|
||||
fee_hash = std_hash(std_hash(target_tip.height))
|
||||
@ -1049,13 +1048,14 @@ class FullNode:
|
||||
Delivery.BROADCAST_TO_OTHERS,
|
||||
)
|
||||
|
||||
# Receives a full transaction
|
||||
# If we added it to mempool send tx id to others
|
||||
# TODO if it's not added ?
|
||||
@api_request
|
||||
async def transaction(
|
||||
self, tx: peer_protocol.Transaction
|
||||
) -> OutboundMessageGenerator:
|
||||
"""
|
||||
Receives a full transaction from peer.
|
||||
If tx is added to mempool, send tx_id to others. (maybe_transaction)
|
||||
"""
|
||||
added, error = await self.mempool.add_spendbundle(tx.sb)
|
||||
if added:
|
||||
maybeTX = peer_protocol.TransactionId(tx.sb.name())
|
||||
@ -1070,13 +1070,14 @@ class FullNode:
|
||||
)
|
||||
return
|
||||
|
||||
# Receives a transaction_id,
|
||||
# Ignore if we've seen it already
|
||||
# Request full transaction if we haven't seen it previously
|
||||
@api_request
|
||||
async def maybe_transaction(
|
||||
self, tx_id: peer_protocol.TransactionId
|
||||
) -> OutboundMessageGenerator:
|
||||
"""
|
||||
Receives a transaction_id, ignore if we've seen it already.
|
||||
Request a full transaction if we haven't seen it previously_id:
|
||||
"""
|
||||
if self.mempool.seen(tx_id.transaction_id):
|
||||
self.log.info(f"tx_id({tx_id.transaction_id}) already seen")
|
||||
return
|
||||
@ -1088,11 +1089,11 @@ class FullNode:
|
||||
Delivery.RESPOND,
|
||||
)
|
||||
|
||||
# Peer has request a full transaction from us
|
||||
@api_request
|
||||
async def request_transaction(
|
||||
self, tx_id: peer_protocol.RequestTransaction
|
||||
) -> OutboundMessageGenerator:
|
||||
""" Peer has request a full transaction from us. """
|
||||
spend_bundle = await self.mempool.get_spendbundle(tx_id.transaction_id)
|
||||
if spend_bundle is None:
|
||||
return
|
||||
|
@ -1,12 +1,12 @@
|
||||
import collections
|
||||
from typing import Dict, Optional, Tuple, List
|
||||
from typing import Dict, Optional, Tuple, List, Set
|
||||
|
||||
from src.consensus.constants import constants as consensus_constants
|
||||
from src.farming.farming_tools import best_solution_program
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.hashable.Coin import CoinName, Coin
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.SpendBundle import SpendBundle
|
||||
from src.types.hashable.Unspent import Unspent
|
||||
from src.types.hashable.CoinRecord import CoinRecord
|
||||
from src.types.header_block import SmallHeaderBlock
|
||||
from src.types.mempool_item import MempoolItem
|
||||
from src.types.pool import Pool
|
||||
@ -22,9 +22,6 @@ from src.util.ints import uint64, uint32
|
||||
from sortedcontainers import SortedDict
|
||||
|
||||
|
||||
MAX_COIN_AMOUNT = int(1 << 48)
|
||||
|
||||
|
||||
class Mempool:
|
||||
def __init__(self, unspent_store: UnspentStore, override_constants: Dict = {}):
|
||||
# Allow passing in custom overrides
|
||||
@ -34,8 +31,8 @@ class Mempool:
|
||||
|
||||
# Transactions that were unable to enter mempool, used for retry. (they were invalid)
|
||||
self.potential_txs: Dict[bytes32, SpendBundle] = {}
|
||||
|
||||
self.allSeen: Dict[bytes32, bytes32] = {}
|
||||
# TODO limit the size of seen_bundle_hashes
|
||||
self.seen_bundle_hashes: Set[bytes32] = set()
|
||||
# Mempool for each tip
|
||||
self.mempools: Dict[bytes32, Pool] = {}
|
||||
|
||||
@ -83,7 +80,7 @@ class Mempool:
|
||||
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.
|
||||
"""
|
||||
self.allSeen[new_spend.name()] = new_spend.name()
|
||||
self.seen_bundle_hashes.add(new_spend.name())
|
||||
|
||||
# Calculate the cost and fees
|
||||
program = best_solution_program(new_spend)
|
||||
@ -104,7 +101,7 @@ class Mempool:
|
||||
|
||||
# Check additions for max coin amount
|
||||
for coin in additions:
|
||||
if coin.amount >= MAX_COIN_AMOUNT:
|
||||
if coin.amount >= consensus_constants["MAX_COIN_AMOUNT"]:
|
||||
return False, Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
||||
|
||||
# Watch out for duplicate outputs
|
||||
@ -173,9 +170,9 @@ class Mempool:
|
||||
hash_key_pairs = []
|
||||
error: Optional[Err] = None
|
||||
for npc in npc_list:
|
||||
uns: Unspent = unspents[npc.coin_name]
|
||||
coin_record: CoinRecord = unspents[npc.coin_name]
|
||||
error = mempool_check_conditions_dict(
|
||||
uns, new_spend, npc.condition_dict, pool
|
||||
coin_record, new_spend, npc.condition_dict, pool
|
||||
)
|
||||
if error:
|
||||
if (
|
||||
@ -213,13 +210,13 @@ class Mempool:
|
||||
|
||||
async def check_removals(
|
||||
self, additions: List[Coin], removals: List[Coin], mempool: Pool
|
||||
) -> Tuple[Optional[Err], Dict[bytes32, Unspent], List[Coin]]:
|
||||
) -> Tuple[Optional[Err], Dict[bytes32, CoinRecord], List[Coin]]:
|
||||
"""
|
||||
This function checks for double spends, unknown spends and conflicting transactions in mempool.
|
||||
Returns Error (if any), dictionary of Unspents, list of coins with conflict errors (if any any).
|
||||
"""
|
||||
removals_counter: Dict[CoinName, int] = {}
|
||||
unspents: Dict[bytes32, Unspent] = {}
|
||||
removals_counter: Dict[bytes32, int] = {}
|
||||
coin_records: Dict[bytes32, CoinRecord] = {}
|
||||
conflicts: List[Coin] = []
|
||||
for removal in removals:
|
||||
# 0. Checks for double spend inside same spend_bundle
|
||||
@ -230,12 +227,12 @@ class Mempool:
|
||||
# 1. Checks if removed coin is created in spend_bundle (For ephemeral coins)
|
||||
if removal in additions:
|
||||
# Setting ephemeral coin confirmed index to current + 1
|
||||
if removal.name() in unspents:
|
||||
if removal.name() in coin_records:
|
||||
return Err.DOUBLE_SPEND, {}, []
|
||||
unspents[removal.name()] = Unspent(removal, mempool.header_block.height + 1, 0, 0, 0) # type: ignore # noqa
|
||||
coin_records[removal.name()] = CoinRecord(removal, mempool.header_block.height + 1, 0, 0, 0) # type: ignore # noqa
|
||||
continue
|
||||
# 2. Checks we have it in the unspent_store
|
||||
unspent: Optional[Unspent] = await self.unspent_store.get_unspent(
|
||||
unspent: Optional[CoinRecord] = await self.unspent_store.get_coin_record(
|
||||
removal.name(), mempool.header
|
||||
)
|
||||
if unspent is None:
|
||||
@ -254,11 +251,11 @@ class Mempool:
|
||||
):
|
||||
return Err.COINBASE_NOT_YET_SPENDABLE, {}, []
|
||||
|
||||
unspents[unspent.coin.name()] = unspent
|
||||
coin_records[unspent.coin.name()] = unspent
|
||||
if len(conflicts) > 0:
|
||||
return Err.MEMPOOL_CONFLICT, unspents, conflicts
|
||||
return Err.MEMPOOL_CONFLICT, coin_records, conflicts
|
||||
# 5. If coins can be spent return list of unspents as we see them in local storage
|
||||
return None, unspents, []
|
||||
return None, coin_records, []
|
||||
|
||||
async def add_to_potential_tx_set(self, spend: SpendBundle):
|
||||
"""
|
||||
@ -273,10 +270,10 @@ class Mempool:
|
||||
|
||||
async def seen(self, bundle_hash: bytes32) -> bool:
|
||||
""" Return true if we saw this spendbundle before """
|
||||
if self.allSeen[bundle_hash] is None:
|
||||
return False
|
||||
else:
|
||||
if bundle_hash in self.seen_bundle_hashes:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
async def get_spendbundle(self, bundle_hash: bytes32) -> Optional[SpendBundle]:
|
||||
""" Returns a full SpendBundle if it's inside one the mempools"""
|
||||
@ -303,7 +300,7 @@ class Mempool:
|
||||
else:
|
||||
# Create mempool for new head
|
||||
if len(self.old_mempools) > 0:
|
||||
new_pool = await Pool.create(
|
||||
new_pool = Pool.create(
|
||||
tip.header_block.to_small(), self.mempool_size
|
||||
)
|
||||
|
||||
@ -317,7 +314,7 @@ class Mempool:
|
||||
|
||||
await self.initialize_pool_from_current_pools(new_pool)
|
||||
else:
|
||||
new_pool = await Pool.create(
|
||||
new_pool = Pool.create(
|
||||
tip.header_block.to_small(), self.mempool_size
|
||||
)
|
||||
await self.initialize_pool_from_current_pools(new_pool)
|
||||
|
@ -2,20 +2,19 @@ import blspy
|
||||
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint64
|
||||
from src.types.hashable.Program import ProgramHash
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
||||
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
|
||||
|
||||
|
||||
def create_puzzlehash_for_pk(pub_key: BLSPublicKey) -> ProgramHash:
|
||||
return ProgramHash(puzzle_for_pk(pub_key))
|
||||
def create_puzzlehash_for_pk(pub_key: BLSPublicKey) -> bytes32:
|
||||
return puzzle_for_pk(pub_key).get_hash()
|
||||
|
||||
|
||||
def signature_for_coinbase(coin: Coin, pool_private_key: blspy.PrivateKey):
|
||||
message_hash = blspy.Util.hash256(bytes(coin))
|
||||
return BLSSignature(
|
||||
pool_private_key.sign_prepend_prehashed(message_hash).serialize()
|
||||
bytes(pool_private_key.sign_prepend_prehashed(message_hash))
|
||||
)
|
||||
|
||||
|
||||
@ -25,14 +24,14 @@ def sign_coinbase_coin(coin: Coin, private_key: blspy.PrivateKey):
|
||||
return signature_for_coinbase(coin, private_key)
|
||||
|
||||
|
||||
def create_coinbase_coin(block_index: int, puzzle_hash: ProgramHash, reward: uint64):
|
||||
def create_coinbase_coin(block_index: int, puzzle_hash: bytes32, reward: uint64):
|
||||
block_index_as_hash = bytes32(block_index.to_bytes(32, "big"))
|
||||
return Coin(block_index_as_hash, puzzle_hash, reward)
|
||||
|
||||
|
||||
def create_coinbase_coin_and_signature(
|
||||
block_index: int,
|
||||
puzzle_hash: ProgramHash,
|
||||
puzzle_hash: bytes32,
|
||||
reward: uint64,
|
||||
private_key: blspy.PrivateKey,
|
||||
):
|
||||
|
@ -291,9 +291,9 @@ class FullNodeStore:
|
||||
body: Body,
|
||||
header: HeaderData,
|
||||
pos: ProofOfSpace,
|
||||
height: int = 0,
|
||||
height: uint32 = uint32(0),
|
||||
):
|
||||
self.candidate_blocks[pos_hash] = (body, header, pos, height) # type: ignore # noqa
|
||||
self.candidate_blocks[pos_hash] = (body, header, pos, height)
|
||||
|
||||
def get_candidate_block(
|
||||
self, pos_hash: bytes32
|
||||
|
@ -7,7 +7,8 @@ from src.types.condition_opcodes import ConditionOpcode
|
||||
@dataclass(frozen=True)
|
||||
class ConditionVarPair:
|
||||
"""
|
||||
This structure is used in the body for the reward and fees genesis coins.
|
||||
This structure is used to store parsed CLVM conditions
|
||||
Conditions in CLVM have either format of (opcode, var1) or (opcode, var1, var2)
|
||||
"""
|
||||
|
||||
opcode: ConditionOpcode
|
||||
|
@ -3,9 +3,7 @@ from typing import List
|
||||
|
||||
import blspy
|
||||
|
||||
from src.types.sized_bytes import bytes48, bytes96
|
||||
|
||||
from .Message import MessageHash
|
||||
from src.types.sized_bytes import bytes48, bytes96, bytes32
|
||||
from src.util.streamable import Streamable, streamable
|
||||
|
||||
ZERO96 = bytes96([0] * 96)
|
||||
@ -26,7 +24,7 @@ class BLSSignature(Streamable):
|
||||
@streamable
|
||||
class AGGSIGPair(Streamable):
|
||||
public_key: BLSPublicKey
|
||||
message_hash: MessageHash
|
||||
message_hash: bytes32
|
||||
|
||||
sig: bytes96
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.atoms import hash_pointer
|
||||
from src.types.hashable.Program import ProgramHash
|
||||
from src.types.hashable.Hash import std_hash
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint64
|
||||
from src.util.streamable import Streamable, streamable
|
||||
@ -15,14 +11,9 @@ class Coin(Streamable):
|
||||
This structure is used in the body for the reward and fees genesis coins.
|
||||
"""
|
||||
|
||||
parent_coin_info: bytes
|
||||
puzzle_hash: ProgramHash
|
||||
parent_coin_info: bytes32
|
||||
puzzle_hash: bytes32
|
||||
amount: uint64
|
||||
|
||||
def name(self) -> "CoinName":
|
||||
return CoinName(self)
|
||||
|
||||
|
||||
CoinName: bytes32 = hash_pointer(Coin, std_hash)
|
||||
|
||||
Coin.__annotations__["parent_coin_info"] = CoinName
|
||||
def name(self) -> bytes32:
|
||||
return self.get_hash()
|
||||
|
@ -8,7 +8,7 @@ from src.util.ints import uint32, uint8
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@streamable
|
||||
class Unspent(Streamable):
|
||||
class CoinRecord(Streamable):
|
||||
"""
|
||||
These are values that correspond to a CoinName that are used
|
||||
in keeping track of the unspent database.
|
@ -1,27 +0,0 @@
|
||||
import blspy
|
||||
|
||||
from ...atoms import hash_pointer
|
||||
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.streamable import Streamable, streamable
|
||||
|
||||
|
||||
def bls_hash(s) -> bytes32:
|
||||
return bytes32(blspy.Util.hash256(s))
|
||||
|
||||
|
||||
@streamable
|
||||
class Message(Streamable):
|
||||
data: bytes
|
||||
|
||||
def stream(self, f):
|
||||
f.write(self.data)
|
||||
|
||||
def __str__(self):
|
||||
return self.data.hex()
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %s>" % (self.__class__.__name__, str(self))
|
||||
|
||||
|
||||
MessageHash = hash_pointer(Message, bls_hash)
|
@ -5,7 +5,7 @@ from clvm.subclass_sexp import BaseSExp
|
||||
from src.types.sized_bytes import bytes32
|
||||
from .Hash import std_hash
|
||||
|
||||
from ...atoms import bin_methods, hash_pointer
|
||||
from ...atoms import bin_methods
|
||||
|
||||
SExp = to_sexp_f(1).__class__
|
||||
|
||||
@ -32,5 +32,5 @@ class Program(SExp, bin_methods): # type: ignore # noqa
|
||||
def __str__(self):
|
||||
return bytes(self).hex()
|
||||
|
||||
|
||||
ProgramHash: bytes32 = hash_pointer(Program, std_hash)
|
||||
def get_hash(self):
|
||||
return bytes32(std_hash(bytes(self)))
|
||||
|
@ -3,7 +3,6 @@ from typing import List, Dict
|
||||
from sortedcontainers import SortedDict
|
||||
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.Coin import CoinName
|
||||
from src.types.header_block import SmallHeaderBlock
|
||||
from src.types.mempool_item import MempoolItem
|
||||
from src.types.sized_bytes import bytes32
|
||||
@ -14,14 +13,14 @@ class Pool:
|
||||
header: SmallHeaderBlock
|
||||
spends: Dict[bytes32, MempoolItem]
|
||||
sorted_spends: SortedDict
|
||||
additions: Dict[CoinName, MempoolItem]
|
||||
removals: Dict[CoinName, MempoolItem]
|
||||
additions: Dict[bytes32, MempoolItem]
|
||||
removals: Dict[bytes32, MempoolItem]
|
||||
min_fee: uint64
|
||||
size: uint32
|
||||
|
||||
# if new min fee is added
|
||||
@staticmethod
|
||||
async def create(tip: SmallHeaderBlock, size: uint32):
|
||||
def create(tip: SmallHeaderBlock, size: uint32):
|
||||
self = Pool()
|
||||
self.header = tip
|
||||
self.spends = {}
|
||||
|
@ -3,8 +3,8 @@ from typing import Dict, Optional, List
|
||||
from pathlib import Path
|
||||
import aiosqlite
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.hashable.Coin import Coin, CoinName
|
||||
from src.types.hashable.Unspent import Unspent
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.CoinRecord import CoinRecord
|
||||
from src.types.header_block import SmallHeaderBlock
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint32, uint8
|
||||
@ -12,10 +12,10 @@ from src.util.ints import uint32, uint8
|
||||
|
||||
class DiffStore:
|
||||
header: SmallHeaderBlock
|
||||
diffs: Dict[CoinName, Unspent]
|
||||
diffs: Dict[bytes32, CoinRecord]
|
||||
|
||||
@staticmethod
|
||||
async def create(head: SmallHeaderBlock, diffs: Dict[CoinName, Unspent]):
|
||||
async def create(head: SmallHeaderBlock, diffs: Dict[bytes32, CoinRecord]):
|
||||
self = DiffStore()
|
||||
self.header = head
|
||||
self.diffs = diffs
|
||||
@ -34,13 +34,15 @@ class UnspentStore:
|
||||
# Whether or not we are syncing
|
||||
sync_mode: bool = False
|
||||
lock: asyncio.Lock
|
||||
lca_unspent_coins: Dict[str, Unspent]
|
||||
lca_unspent_coins: Dict[str, CoinRecord]
|
||||
head_diffs: Dict[bytes32, DiffStore]
|
||||
cache_size: uint32
|
||||
|
||||
@classmethod
|
||||
async def create(cls, db_path: Path):
|
||||
async def create(cls, db_path: Path, cache_size: uint32 = uint32(600000)):
|
||||
self = cls()
|
||||
|
||||
self.cache_size = cache_size
|
||||
# All full blocks which have been added to the blockchain. Header_hash -> block
|
||||
self.unspent_db = await aiosqlite.connect(db_path)
|
||||
await self.unspent_db.execute(
|
||||
@ -94,11 +96,11 @@ class UnspentStore:
|
||||
await self.set_spent(coin_name, block.height)
|
||||
|
||||
for coin in additions:
|
||||
unspent: Unspent = Unspent(coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
unspent: CoinRecord = CoinRecord(coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
await self.add_unspent(unspent)
|
||||
|
||||
coinbase: Unspent = Unspent(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
fees_coin: Unspent = Unspent(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
coinbase: CoinRecord = CoinRecord(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
fees_coin: CoinRecord = CoinRecord(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
await self.add_unspent(coinbase)
|
||||
await self.add_unspent(fees_coin)
|
||||
|
||||
@ -128,14 +130,14 @@ class UnspentStore:
|
||||
):
|
||||
|
||||
for coin_name in removals:
|
||||
removed: Optional[Unspent] = None
|
||||
removed: Optional[CoinRecord] = None
|
||||
if coin_name.hex() in diff_store.diffs:
|
||||
removed = diff_store.diffs[coin_name.hex()]
|
||||
if removed is None:
|
||||
removed = await self.get_unspent(coin_name)
|
||||
removed = await self.get_coin_record(coin_name)
|
||||
if removed is None:
|
||||
raise Exception
|
||||
spent = Unspent(
|
||||
spent = CoinRecord(
|
||||
removed.coin,
|
||||
removed.confirmed_block_index,
|
||||
block.height,
|
||||
@ -145,16 +147,16 @@ class UnspentStore:
|
||||
diff_store.diffs[spent.name.hex()] = spent
|
||||
|
||||
for coin in additions:
|
||||
added: Unspent = Unspent(coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
added: CoinRecord = CoinRecord(coin, block.height, 0, 0, 0) # type: ignore # noqa
|
||||
diff_store.diffs[added.name.hex()] = added
|
||||
|
||||
coinbase: Unspent = Unspent(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
coinbase: CoinRecord = CoinRecord(block.body.coinbase, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
diff_store.diffs[coinbase.name.hex()] = coinbase
|
||||
fees_coin: Unspent = Unspent(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
fees_coin: CoinRecord = CoinRecord(block.body.fees_coin, block.height, 0, 0, 1) # type: ignore # noqa
|
||||
diff_store.diffs[fees_coin.name.hex()] = fees_coin
|
||||
|
||||
# Store unspent in DB and ram cache
|
||||
async def add_unspent(self, unspent: Unspent) -> None:
|
||||
async def add_unspent(self, unspent: CoinRecord) -> None:
|
||||
cursor = await self.unspent_db.execute(
|
||||
"INSERT OR REPLACE INTO unspent VALUES(?, ?, ?, ?, ?, ?)",
|
||||
(
|
||||
@ -169,17 +171,17 @@ class UnspentStore:
|
||||
await cursor.close()
|
||||
await self.unspent_db.commit()
|
||||
self.lca_unspent_coins[unspent.coin.name().hex()] = unspent
|
||||
if len(self.lca_unspent_coins) > 600000:
|
||||
while len(self.lca_unspent_coins) > 600000:
|
||||
if len(self.lca_unspent_coins) > self.cache_size:
|
||||
while len(self.lca_unspent_coins) > self.cache_size:
|
||||
first_in = list(self.lca_unspent_coins.keys())[0]
|
||||
del self.lca_unspent_coins[first_in]
|
||||
|
||||
# Update unspent to be spent in DB
|
||||
async def set_spent(self, coin_name: bytes32, index: uint32):
|
||||
current: Optional[Unspent] = await self.get_unspent(coin_name)
|
||||
current: Optional[CoinRecord] = await self.get_coin_record(coin_name)
|
||||
if current is None:
|
||||
return
|
||||
spent: Unspent = Unspent(
|
||||
spent: CoinRecord = CoinRecord(
|
||||
current.coin,
|
||||
current.confirmed_block_index,
|
||||
index,
|
||||
@ -189,9 +191,9 @@ class UnspentStore:
|
||||
await self.add_unspent(spent)
|
||||
|
||||
# Checks DB and DiffStores for unspent with coin_name and returns it
|
||||
async def get_unspent(
|
||||
self, coin_name: CoinName, header: SmallHeaderBlock = None
|
||||
) -> Optional[Unspent]:
|
||||
async def get_coin_record(
|
||||
self, coin_name: bytes32, header: SmallHeaderBlock = None
|
||||
) -> Optional[CoinRecord]:
|
||||
if header is not None and header.header_hash in self.head_diffs:
|
||||
diff_store = self.head_diffs[header.header_hash]
|
||||
if coin_name.hex() in diff_store.diffs:
|
||||
@ -204,25 +206,29 @@ class UnspentStore:
|
||||
row = await cursor.fetchone()
|
||||
await cursor.close()
|
||||
if row is not None:
|
||||
return Unspent.from_bytes(row[5])
|
||||
return CoinRecord.from_bytes(row[5])
|
||||
return None
|
||||
|
||||
# TODO figure out if we want to really delete when doing rollback
|
||||
async def rollback_lca_to_block(self, block_index):
|
||||
# Update memory cache
|
||||
for k in list(self.lca_unspent_coins.keys()):
|
||||
v = self.lca_unspent_coins[k]
|
||||
if v.spent_block_index > block_index:
|
||||
new_unspent = Unspent(
|
||||
v.coin,
|
||||
v.confirmed_block_index,
|
||||
v.spent_block_index,
|
||||
delete_queue: bytes32 = []
|
||||
for coin_name, coin_record in self.lca_unspent_coins.items():
|
||||
if coin_record.spent_block_index > block_index:
|
||||
new_unspent = CoinRecord(
|
||||
coin_record.coin,
|
||||
coin_record.confirmed_block_index,
|
||||
coin_record.spent_block_index,
|
||||
uint8(0),
|
||||
v.coinbase,
|
||||
coin_record.coinbase,
|
||||
)
|
||||
self.lca_unspent_coins[v.coin.name().hex()] = new_unspent
|
||||
if v.confirmed_block_index > block_index:
|
||||
del self.lca_unspent_coins[k]
|
||||
self.lca_unspent_coins[coin_record.coin.name().hex()] = new_unspent
|
||||
if coin_record.confirmed_block_index > block_index:
|
||||
delete_queue.append(coin_name)
|
||||
|
||||
for coin_name in delete_queue:
|
||||
del self.lca_unspent_coins[coin_name]
|
||||
|
||||
# Delete from storage
|
||||
c1 = await self.unspent_db.execute(
|
||||
"DELETE FROM unspent WHERE confirmed_index>?", (block_index,)
|
||||
|
@ -4,7 +4,7 @@ from typing import Optional, Dict, List
|
||||
from clvm.casts import int_from_bytes
|
||||
|
||||
from src.types.ConditionVarPair import ConditionVarPair
|
||||
from src.types.hashable.Unspent import Unspent
|
||||
from src.types.hashable.CoinRecord import CoinRecord
|
||||
from src.types.header_block import SmallHeaderBlock
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.Conditions import ConditionOpcode
|
||||
@ -13,7 +13,7 @@ from src.util.ints import uint64
|
||||
|
||||
|
||||
def blockchain_assert_coin_consumed(
|
||||
condition: ConditionVarPair, removed: Dict[bytes32, Unspent]
|
||||
condition: ConditionVarPair, removed: Dict[bytes32, CoinRecord]
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks coin consumed conditions
|
||||
@ -26,7 +26,7 @@ def blockchain_assert_coin_consumed(
|
||||
|
||||
|
||||
def blockchain_assert_my_coin_id(
|
||||
condition: ConditionVarPair, unspent: Unspent
|
||||
condition: ConditionVarPair, unspent: CoinRecord
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks if CoinID matches the id from the condition
|
||||
@ -53,7 +53,7 @@ def blockchain_assert_block_index_exceeds(
|
||||
|
||||
|
||||
def blockchain_assert_block_age_exceeds(
|
||||
condition: ConditionVarPair, unspent: Unspent, header: SmallHeaderBlock
|
||||
condition: ConditionVarPair, unspent: CoinRecord, header: SmallHeaderBlock
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks if the coin age exceeds the age from the condition
|
||||
@ -84,8 +84,8 @@ def blockchain_assert_time_exceeds(condition: ConditionVarPair):
|
||||
|
||||
|
||||
def blockchain_check_conditions_dict(
|
||||
unspent: Unspent,
|
||||
removed: Dict[bytes32, Unspent],
|
||||
unspent: CoinRecord,
|
||||
removed: Dict[bytes32, CoinRecord],
|
||||
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]],
|
||||
header: SmallHeaderBlock,
|
||||
) -> Optional[Err]:
|
||||
|
@ -1,5 +1,6 @@
|
||||
from typing import List, Tuple, Optional, Dict
|
||||
|
||||
import blspy
|
||||
import clvm
|
||||
from clvm.EvalError import EvalError
|
||||
from clvm.casts import int_from_bytes
|
||||
@ -8,7 +9,6 @@ from src.types.ConditionVarPair import ConditionVarPair
|
||||
from src.types.condition_opcodes import ConditionOpcode
|
||||
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.Message import MessageHash
|
||||
from src.types.hashable.Program import Program
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ConsensusError import Err
|
||||
@ -83,6 +83,6 @@ def hash_key_pairs_for_conditions_dict(
|
||||
# TODO: check types
|
||||
# assert len(_) == 3
|
||||
blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
|
||||
message: MessageHash = MessageHash(cvp.var2)
|
||||
message: bytes32 = bytes32(blspy.Util.hash256(cvp.var2))
|
||||
pairs.append(BLSSignature.AGGSIGPair(blspubkey, message))
|
||||
return pairs
|
||||
|
@ -5,12 +5,12 @@ from clvm import EvalError
|
||||
from clvm.casts import int_from_bytes
|
||||
|
||||
from src.types.ConditionVarPair import ConditionVarPair
|
||||
from src.types.hashable.Coin import CoinName
|
||||
from src.types.hashable.Program import Program, ProgramHash
|
||||
from src.types.hashable.Program import Program
|
||||
from src.types.hashable.SpendBundle import SpendBundle
|
||||
from src.types.hashable.Unspent import Unspent
|
||||
from src.types.hashable.CoinRecord import CoinRecord
|
||||
from src.types.name_puzzle_condition import NPC
|
||||
from src.types.pool import Pool
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.Conditions import ConditionOpcode
|
||||
from src.util.ConsensusError import Err
|
||||
from src.util.consensus import conditions_dict_for_solution
|
||||
@ -35,7 +35,7 @@ def mempool_assert_coin_consumed(
|
||||
|
||||
|
||||
def mempool_assert_my_coin_id(
|
||||
condition: ConditionVarPair, unspent: Unspent
|
||||
condition: ConditionVarPair, unspent: CoinRecord
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks if CoinID matches the id from the condition
|
||||
@ -46,7 +46,7 @@ def mempool_assert_my_coin_id(
|
||||
|
||||
|
||||
def mempool_assert_block_index_exceeds(
|
||||
condition: ConditionVarPair, unspent: Unspent, mempool: Pool
|
||||
condition: ConditionVarPair, unspent: CoinRecord, mempool: Pool
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks if the next block index exceeds the block index from the condition
|
||||
@ -62,7 +62,7 @@ def mempool_assert_block_index_exceeds(
|
||||
|
||||
|
||||
def mempool_assert_block_age_exceeds(
|
||||
condition: ConditionVarPair, unspent: Unspent, mempool: Pool
|
||||
condition: ConditionVarPair, unspent: CoinRecord, mempool: Pool
|
||||
) -> Optional[Err]:
|
||||
"""
|
||||
Checks if the coin age exceeds the age from the condition
|
||||
@ -113,12 +113,12 @@ async def get_name_puzzle_conditions(
|
||||
return Err.INVALID_COIN_SOLUTION, [], cost
|
||||
if not isinstance(_[0], bytes) or len(_[0]) != 32:
|
||||
return Err.INVALID_COIN_SOLUTION, [], cost
|
||||
coin_name = CoinName(_[0])
|
||||
coin_name = bytes32(_[0])
|
||||
if not isinstance(_[1], list) or len(_[1]) != 2:
|
||||
return Err.INVALID_COIN_SOLUTION, [], cost
|
||||
puzzle_solution_program = name_solution.rest().first()
|
||||
puzzle_program = puzzle_solution_program.first()
|
||||
puzzle_hash = ProgramHash(Program(puzzle_program))
|
||||
puzzle_hash = Program(puzzle_program).get_hash()
|
||||
try:
|
||||
error, conditions_dict = conditions_dict_for_solution(
|
||||
puzzle_solution_program
|
||||
@ -136,7 +136,7 @@ async def get_name_puzzle_conditions(
|
||||
|
||||
|
||||
def mempool_check_conditions_dict(
|
||||
unspent: Unspent,
|
||||
unspent: CoinRecord,
|
||||
spend_bundle: SpendBundle,
|
||||
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]],
|
||||
mempool: Pool,
|
||||
|
@ -9,7 +9,7 @@ import clvm
|
||||
|
||||
from clvm_tools import binutils
|
||||
|
||||
from src.types.hashable.Program import Program, ProgramHash
|
||||
from src.types.hashable.Program import Program
|
||||
|
||||
"""
|
||||
solution: (puzzle_reveal . solution_to_puzzle)
|
||||
@ -28,6 +28,6 @@ def puzzle_for_puzzle_hash(underlying_puzzle_hash):
|
||||
|
||||
|
||||
def solution_for_puzzle_and_solution(underlying_puzzle, underlying_solution):
|
||||
underlying_puzzle_hash = ProgramHash(underlying_puzzle)
|
||||
underlying_puzzle_hash = underlying_puzzle.get_hash()
|
||||
puzzle_program = puzzle_for_puzzle_hash(underlying_puzzle_hash)
|
||||
return Program.to([puzzle_program, underlying_solution])
|
||||
|
@ -399,13 +399,12 @@ class BlockTools:
|
||||
solutions_generator: bytes32 = sha256(seed).digest()
|
||||
cost = uint64(0)
|
||||
|
||||
block_reward = block_rewards.calculate_block_reward(height)
|
||||
if genesis:
|
||||
coinbase_reward = block_reward
|
||||
coinbase_reward = block_rewards.calculate_block_reward(height)
|
||||
fee_reward = 0
|
||||
else:
|
||||
coinbase_reward = uint64(int((block_reward / 8) * 7))
|
||||
fee_reward = uint64(int(block_reward / 8))
|
||||
coinbase_reward = block_rewards.calculate_block_reward(height)
|
||||
fee_reward = block_rewards.calculate_base_fee(height)
|
||||
|
||||
coinbase_coin, coinbase_signature = create_coinbase_coin_and_signature(
|
||||
height, reward_puzzlehash, coinbase_reward, pool_sk
|
||||
|
@ -2,7 +2,6 @@ import blspy
|
||||
|
||||
from src.types.hashable.CoinSolution import CoinSolution
|
||||
from src.types.hashable.SpendBundle import SpendBundle
|
||||
from src.types.hashable.Program import ProgramHash
|
||||
|
||||
from src.wallet.BLSPrivateKey import BLSPrivateKey
|
||||
from src.wallet.keychain import Keychain
|
||||
@ -27,7 +26,7 @@ def puzzle_program_for_index(index):
|
||||
|
||||
|
||||
def puzzle_hash_for_index(index):
|
||||
return ProgramHash(puzzle_program_for_index(index))
|
||||
return puzzle_program_for_index(index).get_hash()
|
||||
|
||||
|
||||
def conditions_for_payment(puzzle_hash_amount_pairs):
|
||||
|
@ -88,7 +88,7 @@ class TestBlockchainTransactions:
|
||||
# Two coins are added, main spend and change
|
||||
assert len(added_coins) == 2
|
||||
for coin in added_coins:
|
||||
unspent = await full_node_1.unspent_store.get_unspent(
|
||||
unspent = await full_node_1.unspent_store.get_coin_record(
|
||||
coin.name(), next_block.header_block
|
||||
)
|
||||
assert unspent is not None
|
||||
|
@ -43,8 +43,8 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
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
|
||||
|
||||
@ -60,15 +60,15 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
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
|
||||
|
||||
await db.set_spent(unspent.coin.name(), block.height)
|
||||
await db.set_spent(unspent_fee.coin.name(), block.height)
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
|
||||
@ -84,15 +84,15 @@ class TestUnspent:
|
||||
# Save/get block
|
||||
for block in blocks:
|
||||
await db.new_lca(block)
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
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
|
||||
|
||||
await db.set_spent(unspent.coin.name(), block.height)
|
||||
await db.set_spent(unspent_fee.coin.name(), block.height)
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
|
||||
@ -100,8 +100,8 @@ class TestUnspent:
|
||||
await db.rollback_lca_to_block(reorg_index)
|
||||
|
||||
for c, block in enumerate(blocks):
|
||||
unspent = await db.get_unspent(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_unspent(block.body.fees_coin.name())
|
||||
unspent = await db.get_coin_record(block.body.coinbase.name())
|
||||
unspent_fee = await db.get_coin_record(block.body.fees_coin.name())
|
||||
if c <= reorg_index:
|
||||
assert unspent.spent == 1
|
||||
assert unspent_fee.spent == 1
|
||||
@ -124,10 +124,10 @@ class TestUnspent:
|
||||
assert b.get_current_tips()[0].height == 100
|
||||
|
||||
for c, block in enumerate(blocks):
|
||||
unspent = await unspent_store.get_unspent(
|
||||
unspent = await unspent_store.get_coin_record(
|
||||
block.body.coinbase.name(), block.header_block
|
||||
)
|
||||
unspent_fee = await unspent_store.get_unspent(
|
||||
unspent_fee = await unspent_store.get_coin_record(
|
||||
block.body.fees_coin.name(), block.header_block
|
||||
)
|
||||
assert unspent.spent == 0
|
||||
@ -152,7 +152,7 @@ class TestUnspent:
|
||||
assert result == ReceiveBlockResult.ADDED_AS_ORPHAN
|
||||
elif reorg_block.height >= 100:
|
||||
assert result == ReceiveBlockResult.ADDED_TO_HEAD
|
||||
unspent = await unspent_store.get_unspent(
|
||||
unspent = await unspent_store.get_coin_record(
|
||||
reorg_block.body.coinbase.name(), reorg_block.header_block
|
||||
)
|
||||
assert unspent.name == reorg_block.body.coinbase.name()
|
||||
|
@ -6,7 +6,7 @@ from blspy import ExtendedPrivateKey
|
||||
|
||||
from src.types.ConditionVarPair import ConditionVarPair
|
||||
from src.types.condition_opcodes import ConditionOpcode
|
||||
from src.types.hashable.Program import Program, ProgramHash
|
||||
from src.types.hashable.Program import Program
|
||||
from src.types.hashable.BLSSignature import BLSSignature
|
||||
from src.types.hashable.Coin import Coin
|
||||
from src.types.hashable.CoinSolution import CoinSolution
|
||||
@ -58,13 +58,11 @@ class WalletTool:
|
||||
return any(
|
||||
map(
|
||||
lambda child: hash
|
||||
== ProgramHash(
|
||||
puzzle_for_pk(
|
||||
== puzzle_for_pk(
|
||||
self.extended_secret_key.public_child(child)
|
||||
.get_public_key()
|
||||
.serialize()
|
||||
)
|
||||
),
|
||||
).get_hash(),
|
||||
reversed(range(self.next_address)),
|
||||
)
|
||||
)
|
||||
@ -72,7 +70,7 @@ class WalletTool:
|
||||
def get_keys(self, hash):
|
||||
for child in range(self.next_address):
|
||||
pubkey = self.extended_secret_key.public_child(child).get_public_key()
|
||||
if hash == ProgramHash(puzzle_for_pk(pubkey.serialize())):
|
||||
if hash == puzzle_for_pk(pubkey.serialize()).get_hash():
|
||||
return (
|
||||
pubkey,
|
||||
self.extended_secret_key.private_child(child).get_private_key(),
|
||||
@ -88,7 +86,7 @@ class WalletTool:
|
||||
|
||||
def get_new_puzzlehash(self):
|
||||
puzzle = self.get_new_puzzle()
|
||||
puzzlehash = ProgramHash(puzzle)
|
||||
puzzlehash = puzzle.get_hash()
|
||||
return puzzlehash
|
||||
|
||||
def sign(self, value, pubkey):
|
||||
|
Loading…
Reference in New Issue
Block a user