Merge branch 'integration' of github.com:Chia-Network/chia-blockchain into new-protocol

This commit is contained in:
Mariano Sorgente 2020-02-06 19:18:52 -05:00
commit a04282a916
No known key found for this signature in database
GPG Key ID: 0F866338C369278C
26 changed files with 178 additions and 202 deletions

View File

@ -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:

View File

@ -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(),
)

View File

@ -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)

View File

@ -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,
}

View File

@ -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"]),

View File

@ -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

View File

@ -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)

View File

@ -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,
):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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.

View File

@ -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)

View File

@ -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)))

View File

@ -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 = {}

View File

@ -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,)

View File

@ -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]:

View File

@ -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

View File

@ -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,

View File

@ -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])

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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()

View File

@ -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):