Skip to content

Commit

Permalink
ok2 (#3129)
Browse files Browse the repository at this point in the history
* Added new issue templates (#3033)

* added new issue templates for support and requestes

* spelling is hard...

* Add lock, keep cache consistent (#3051)

* execute task decorator

* use blockchain lock

* indentation

* lint

* execute_task

* Ms.empty blocks3 (#3064)

* Fix empty blocks

* Remove error log

* Missing imports

* .header_hash instead of .prev_hash in simulator

* Fix typo (#3069)

* Use secure coin ids (#3095)

* Add announcements for standard tx (#3023)

* changed bahviour of wallet and wallet tools for standard solutions

* black formatting wallet_tools

* added a test for hypothetical stolen zero_output coin

* swap to sha256 from sha256tree for message

* fix wallet_tools version, address lint issues

* correctly int_from_bytes

* fix uninstantiated key in dict

* Comment out broken test

* Fix types (used SerializedProgram)

Co-authored-by: Mariano <sorgente711@gmail.com>

* cache VDF validation results (per process) (#3110)

Co-authored-by: J Eckert <sargonas@users.noreply.github.com>
Co-authored-by: Yostra <straya@chia.net>
Co-authored-by: Mariano Sorgente <3069354+mariano54@users.noreply.github.com>
Co-authored-by: Antonio Borrero Granell <me@antoniobg.com>
Co-authored-by: matt-o-how <48453825+matt-o-how@users.noreply.github.com>
Co-authored-by: Mariano <sorgente711@gmail.com>
Co-authored-by: Arvid Norberg <arvid@libtorrent.org>
  • Loading branch information
8 people authored Apr 29, 2021
1 parent 8b2d0a8 commit 116d5df
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 105 deletions.
2 changes: 1 addition & 1 deletion chia/full_node/mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ async def add_spendbundle(
if npc_result.error is not None:
return None, MempoolInclusionStatus.FAILED, Err(npc_result.error)
# build removal list
removal_names: List[bytes32] = new_spend.removal_names()
removal_names: List[bytes32] = [npc.coin_name for npc in npc_list]

additions = additions_for_npc(npc_list)

Expand Down
49 changes: 28 additions & 21 deletions chia/types/blockchain_format/vdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,46 @@
import traceback
from dataclasses import dataclass
from enum import IntEnum
from typing import Dict, Optional, Tuple
from typing import Optional
from functools import lru_cache

from chiavdf import create_discriminant, verify_n_wesolowski

from chia.consensus.constants import ConsensusConstants
from chia.types.blockchain_format.classgroup import ClassgroupElement
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
from chia.util.ints import uint8, uint64
from chia.util.streamable import Streamable, streamable

log = logging.getLogger(__name__)

discriminant_cache: Dict[Tuple[bytes32, int], int] = {}

@lru_cache(maxsize=20)
def get_discriminant(challenge, size_bites) -> int:
return int(
create_discriminant(challenge, size_bites),
16,
)

def add_to_cache(challenge_size, dsc):
if len(discriminant_cache.keys()) > 10:
keys = list(discriminant_cache.keys())
for i in range(0, 5):
discriminant_cache.pop(keys[i])
discriminant_cache[challenge_size] = dsc

@lru_cache(maxsize=100)
def verify_vdf(
disc: int,
input_el: bytes100,
output: bytes,
number_of_iterations: uint64,
discriminant_size: int,
witness_type: uint8,
):

def get_discriminant(challenge, size_bites):
if (challenge, size_bites) in discriminant_cache:
return discriminant_cache[(challenge, size_bites)]
else:
dsc = int(
create_discriminant(challenge, size_bites),
16,
)
add_to_cache((challenge, size_bites), dsc)
return dsc
return verify_n_wesolowski(
str(disc),
input_el,
output,
number_of_iterations,
discriminant_size,
witness_type,
)


@dataclass(frozen=True)
Expand Down Expand Up @@ -71,8 +78,8 @@ def is_valid(
try:
disc: int = get_discriminant(info.challenge, constants.DISCRIMINANT_SIZE_BITS)
# TODO: parallelize somehow, this might included multiple mini proofs (n weso)
return verify_n_wesolowski(
str(disc),
return verify_vdf(
disc,
input_el.data,
info.output.data + bytes(self.witness),
info.number_of_iterations,
Expand Down
22 changes: 2 additions & 20 deletions chia/types/spend_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,37 +40,19 @@ def additions(self) -> List[Coin]:
return items

def removals(self) -> List[Coin]:
""" This should be used only by wallet"""
"""This should be used only by wallet"""
return [_.coin for _ in self.coin_solutions]

def fees(self) -> int:
""" Unsafe to use for fees validation!!! """
"""Unsafe to use for fees validation!!!"""
amount_in = sum(_.amount for _ in self.removals())
amount_out = sum(_.amount for _ in self.additions())

return amount_in - amount_out

def removal_names(self) -> List[bytes32]:
return [_.coin.name() for _ in self.coin_solutions]

def addition_names(self) -> List[bytes32]:
return [_.name() for _ in self.additions()]

def name(self) -> bytes32:
return self.get_hash()

def not_ephemeral_spends(self):
all_removals = self.removals()
all_additions = self.additions()
result: List[Coin] = []

for rem in all_removals:
if rem in all_additions:
continue
result.append(rem)

return result

def not_ephemeral_additions(self):
all_removals = self.removals()
all_additions = self.additions()
Expand Down
4 changes: 2 additions & 2 deletions chia/util/keychain.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def bytes_to_mnemonic(mnemonic_bytes: bytes):
start = i * 11
end = start + 11
bits = bitarray[start:end]
m_word_poition = bits.uint
m_word = word_list[m_word_poition]
m_word_position = bits.uint
m_word = word_list[m_word_position]
mnemonics.append(m_word)

return " ".join(mnemonics)
Expand Down
23 changes: 19 additions & 4 deletions chia/util/wallet_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from blspy import AugSchemeMPL, G2Element, PrivateKey

from chia.consensus.constants import ConsensusConstants
from chia.util.hash import std_hash
from chia.types.announcement import Announcement
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
Expand Down Expand Up @@ -148,6 +150,8 @@ def generate_unsigned_transaction(

if ConditionOpcode.CREATE_COIN not in condition_dic:
condition_dic[ConditionOpcode.CREATE_COIN] = []
if ConditionOpcode.CREATE_COIN_ANNOUNCEMENT not in condition_dic:
condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT] = []

output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [new_puzzle_hash, int_to_bytes(amount)])
condition_dic[output.opcode].append(output)
Expand All @@ -157,20 +161,31 @@ def generate_unsigned_transaction(
change_puzzle_hash = self.get_new_puzzlehash()
change_output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [change_puzzle_hash, int_to_bytes(change)])
condition_dic[output.opcode].append(change_output)
main_solution: Program = self.make_solution(condition_dic)
else:
main_solution = self.make_solution(condition_dic)

secondary_coins_cond_dic: Dict[ConditionOpcode, List[ConditionWithArgs]] = dict()
secondary_coins_cond_dic[ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT] = []
for n, coin in enumerate(coins):
puzzle_hash = coin.puzzle_hash
if secret_key is None:
secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash)
pubkey = secret_key.get_g1()
puzzle = puzzle_for_pk(bytes(pubkey))
if n == 0:
message_list = [c.name() for c in coins]
for outputs in condition_dic[ConditionOpcode.CREATE_COIN]:
message_list.append(Coin(coin.name(), outputs.vars[0], int_from_bytes(outputs.vars[1])).name())
message = std_hash(b"".join(message_list))
condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT].append(
ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [message])
)
primary_announcement_hash = Announcement(coin.name(), message).name()
secondary_coins_cond_dic[ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT].append(
ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [primary_announcement_hash])
)
main_solution = self.make_solution(condition_dic)
spends.append(CoinSolution(coin, puzzle, main_solution))
else:
spends.append(CoinSolution(coin, puzzle, self.make_solution({})))
spends.append(CoinSolution(coin, puzzle, self.make_solution(secondary_coins_cond_dic)))
return spends

def sign_transaction(self, coin_solutions: List[CoinSolution]) -> SpendBundle:
Expand Down
41 changes: 26 additions & 15 deletions chia/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
from chia.full_node.bundle_tools import simple_solution_generator
from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.program import Program, SerializedProgram
from chia.types.announcement import Announcement
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.coin_solution import CoinSolution
from chia.types.generator_types import BlockGenerator
from chia.types.spend_bundle import SpendBundle
from chia.util.ints import uint8, uint32, uint64, uint128
from chia.util.hash import std_hash
from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
DEFAULT_HIDDEN_PUZZLE_HASH,
Expand Down Expand Up @@ -195,8 +197,8 @@ def make_solution(
primaries: Optional[List[Dict[str, Any]]] = None,
min_time=0,
me=None,
coin_announcements=None,
coin_announcements_to_assert=None,
coin_announcements: Optional[List[bytes32]] = None,
coin_announcements_to_assert: Optional[List[bytes32]] = None,
puzzle_announcements=None,
puzzle_announcements_to_assert=None,
fee=0,
Expand Down Expand Up @@ -227,7 +229,7 @@ def make_solution(
return solution_for_conditions(condition_list)

async def select_coins(self, amount, exclude: List[Coin] = None) -> Set[Coin]:
""" Returns a set of coins that can be used for generating a new transaction. """
"""Returns a set of coins that can be used for generating a new transaction."""
async with self.wallet_state_manager.lock:
if exclude is None:
exclude = []
Expand Down Expand Up @@ -294,7 +296,7 @@ async def generate_unsigned_transaction(
Generates a unsigned transaction in form of List(Puzzle, Solutions)
"""
if primaries_input is None:
primaries = None
primaries: Optional[List[Dict]] = None
total_amount = amount + fee
else:
primaries = primaries_input.copy()
Expand All @@ -315,9 +317,10 @@ async def generate_unsigned_transaction(
self.log.info(f"coins is not None {coins}")
spend_value = sum([coin.amount for coin in coins])
change = spend_value - total_amount
assert change >= 0

spends: List[CoinSolution] = []
output_created = False
primary_announcement_hash: Optional[bytes32] = None

# Check for duplicates
if primaries is not None:
Expand All @@ -330,20 +333,28 @@ async def generate_unsigned_transaction(
puzzle: Program = await self.puzzle_for_puzzle_hash(coin.puzzle_hash)

# Only one coin creates outputs
if not output_created and origin_id in (None, coin.name()):
if primary_announcement_hash is None and origin_id in (None, coin.name()):
if primaries is None:
primaries = [{"puzzlehash": newpuzzlehash, "amount": amount}]
else:
primaries.append({"puzzlehash": newpuzzlehash, "amount": amount})
if change > 0:
changepuzzlehash = await self.get_new_puzzlehash()
primaries.append({"puzzlehash": changepuzzlehash, "amount": change})
solution = self.make_solution(primaries=primaries, fee=fee)
output_created = True
change_puzzle_hash: bytes32 = await self.get_new_puzzlehash()
primaries.append({"puzzlehash": change_puzzle_hash, "amount": change})
message_list: List[bytes32] = [c.name() for c in coins]
for primary in primaries:
message_list.append(Coin(coin.name(), primary["puzzlehash"], primary["amount"]).name())
message: bytes32 = std_hash(b"".join(message_list))
solution: Program = self.make_solution(primaries=primaries, fee=fee, coin_announcements=[message])
primary_announcement_hash = Announcement(coin.name(), message).name()
else:
solution = self.make_solution()
solution = self.make_solution(coin_announcements_to_assert=[primary_announcement_hash])

spends.append(CoinSolution(coin, puzzle, solution))
spends.append(
CoinSolution(
coin, SerializedProgram.from_bytes(bytes(puzzle)), SerializedProgram.from_bytes(bytes(solution))
)
)

self.log.info(f"Spends is {spends}")
return spends
Expand All @@ -366,7 +377,7 @@ async def generate_signed_transaction(
primaries: Optional[List[Dict[str, bytes32]]] = None,
ignore_max_send_amount: bool = False,
) -> TransactionRecord:
""" Use this to generate transaction. """
"""Use this to generate transaction."""
if primaries is None:
non_change_amount = amount
else:
Expand Down Expand Up @@ -410,7 +421,7 @@ async def generate_signed_transaction(
)

async def push_transaction(self, tx: TransactionRecord) -> None:
""" Use this API to send transactions. """
"""Use this API to send transactions."""
await self.wallet_state_manager.add_pending_transaction(tx)

# This is to be aggregated together with a coloured coin offer to ensure that the trade happens
Expand Down
83 changes: 42 additions & 41 deletions tests/blockchain/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2031,47 +2031,48 @@ async def test_max_coin_amount(self):
# 10
# TODO: fix, this is not reaching validation. Because we can't create a block with such amounts due to uint64
# limit in Coin

new_test_constants = test_constants.replace(
**{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
)
b, connection, db_path = await create_blockchain(new_test_constants)
bt_2 = BlockTools(new_test_constants)
bt_2.constants = bt_2.constants.replace(
**{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
)
blocks = bt_2.get_consecutive_blocks(
3,
guarantee_transaction_block=True,
farmer_reward_puzzle_hash=bt.pool_ph,
pool_reward_puzzle_hash=bt.pool_ph,
)
assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK

wt: WalletTool = bt_2.get_pool_wallet_tool()

condition_dict = {ConditionOpcode.CREATE_COIN: []}
output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt_2.pool_ph, int_to_bytes(2 ** 64)])
condition_dict[ConditionOpcode.CREATE_COIN].append(output)

tx: SpendBundle = wt.generate_signed_transaction_multiple_coins(
10,
wt.get_new_puzzlehash(),
list(blocks[1].get_included_reward_coins()),
condition_dic=condition_dict,
)
try:
blocks = bt_2.get_consecutive_blocks(
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
)
assert False
except Exception as e:
pass
await connection.close()
b.shut_down()
db_path.unlink()
pass
#
# new_test_constants = test_constants.replace(
# **{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
# )
# b, connection, db_path = await create_blockchain(new_test_constants)
# bt_2 = BlockTools(new_test_constants)
# bt_2.constants = bt_2.constants.replace(
# **{"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bt.pool_ph, "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bt.pool_ph}
# )
# blocks = bt_2.get_consecutive_blocks(
# 3,
# guarantee_transaction_block=True,
# farmer_reward_puzzle_hash=bt.pool_ph,
# pool_reward_puzzle_hash=bt.pool_ph,
# )
# assert (await b.receive_block(blocks[0]))[0] == ReceiveBlockResult.NEW_PEAK
# assert (await b.receive_block(blocks[1]))[0] == ReceiveBlockResult.NEW_PEAK
# assert (await b.receive_block(blocks[2]))[0] == ReceiveBlockResult.NEW_PEAK
#
# wt: WalletTool = bt_2.get_pool_wallet_tool()
#
# condition_dict = {ConditionOpcode.CREATE_COIN: []}
# output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt_2.pool_ph, int_to_bytes(2 ** 64)])
# condition_dict[ConditionOpcode.CREATE_COIN].append(output)
#
# tx: SpendBundle = wt.generate_signed_transaction_multiple_coins(
# 10,
# wt.get_new_puzzlehash(),
# list(blocks[1].get_included_reward_coins()),
# condition_dic=condition_dict,
# )
# try:
# blocks = bt_2.get_consecutive_blocks(
# 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
# )
# assert False
# except Exception as e:
# pass
# await connection.close()
# b.shut_down()
# db_path.unlink()

@pytest.mark.asyncio
async def test_invalid_merkle_roots(self, empty_blockchain):
Expand Down
Loading

0 comments on commit 116d5df

Please sign in to comment.