Merge branch 'main' into jn.sim-cli-flake-fix
This commit is contained in:
commit
c0ce7b7b71
|
@ -72,7 +72,7 @@ from chia.wallet.trading.offer import Offer
|
|||
from chia.wallet.transaction_record import TransactionRecord
|
||||
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
||||
from chia.wallet.util.address_type import AddressType, is_valid_address
|
||||
from chia.wallet.util.compute_hints import compute_coin_hints
|
||||
from chia.wallet.util.compute_hints import compute_spend_hints_and_additions
|
||||
from chia.wallet.util.compute_memos import compute_memos
|
||||
from chia.wallet.util.query_filter import HashFilter, TransactionTypeFilter
|
||||
from chia.wallet.util.transaction_type import CLAWBACK_TRANSACTION_TYPES, TransactionType
|
||||
|
@ -1981,27 +1981,25 @@ class WalletRpcApi:
|
|||
return {"success": False, "error": "The coin is not a DID."}
|
||||
p2_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata = curried_args
|
||||
|
||||
hint_list = compute_coin_hints(coin_spend)
|
||||
derivation_record = None
|
||||
hinted_coins = compute_spend_hints_and_additions(coin_spend)
|
||||
# Hint is required, if it doesn't have any hint then it should be invalid
|
||||
is_invalid = len(hint_list) == 0
|
||||
for hint in hint_list:
|
||||
derivation_record = (
|
||||
await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
|
||||
bytes32(hint)
|
||||
)
|
||||
)
|
||||
if derivation_record is not None:
|
||||
is_invalid = False
|
||||
hint: Optional[bytes32] = None
|
||||
for hinted_coin in hinted_coins.values():
|
||||
if hinted_coin.coin.amount % 2 == 1 and hinted_coin.hint is not None:
|
||||
hint = hinted_coin.hint
|
||||
break
|
||||
is_invalid = True
|
||||
if is_invalid:
|
||||
if hint is None:
|
||||
# This is an invalid DID, check if we are owner
|
||||
derivation_record = (
|
||||
await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
|
||||
p2_puzzle.get_tree_hash()
|
||||
)
|
||||
)
|
||||
else:
|
||||
derivation_record = (
|
||||
await self.service.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(hint)
|
||||
)
|
||||
|
||||
launcher_id = singleton_struct.rest().first().as_python()
|
||||
if derivation_record is None:
|
||||
return {"success": False, "error": f"This DID {launcher_id.hex()} is not belong to the connected wallet"}
|
||||
|
|
|
@ -1,22 +1,37 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional
|
||||
|
||||
from chia.types.blockchain_format.program import INFINITE_COST
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.program import INFINITE_COST, Program
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.coin_spend import CoinSpend
|
||||
from chia.types.condition_opcodes import ConditionOpcode
|
||||
from chia.util.ints import uint64
|
||||
|
||||
|
||||
def compute_coin_hints(cs: CoinSpend) -> List[bytes32]:
|
||||
@dataclass(frozen=True)
|
||||
class HintedCoin:
|
||||
coin: Coin
|
||||
hint: Optional[bytes32]
|
||||
|
||||
|
||||
def compute_spend_hints_and_additions(cs: CoinSpend) -> Dict[bytes32, HintedCoin]:
|
||||
_, result_program = cs.puzzle_reveal.run_with_cost(INFINITE_COST, cs.solution)
|
||||
|
||||
h_list: List[bytes32] = []
|
||||
for condition_data in result_program.as_python():
|
||||
condition = condition_data[0]
|
||||
args = condition_data[1:]
|
||||
if condition == ConditionOpcode.CREATE_COIN and len(args) > 2:
|
||||
if isinstance(args[2], list):
|
||||
if isinstance(args[2][0], bytes):
|
||||
h_list.append(bytes32(args[2][0]))
|
||||
return h_list
|
||||
hinted_coins: Dict[bytes32, HintedCoin] = {}
|
||||
for condition in result_program.as_iter():
|
||||
if condition.at("f").atom == ConditionOpcode.CREATE_COIN: # It's a create coin:
|
||||
coin: Coin = Coin(cs.coin.name(), bytes32(condition.at("rf").atom), uint64(condition.at("rrf").as_int()))
|
||||
hint: Optional[bytes32] = None
|
||||
if (
|
||||
condition.at("rrr") != Program.to(None) # There's more than two arguments
|
||||
and condition.at("rrrf").atom is None # The 3rd argument is a cons
|
||||
):
|
||||
potential_hint: bytes = condition.at("rrrff").atom
|
||||
if len(potential_hint) == 32:
|
||||
hint = bytes32(potential_hint)
|
||||
hinted_coins[bytes32(coin.name())] = HintedCoin(coin, hint)
|
||||
|
||||
return hinted_coins
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import dataclasses
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
from typing import TYPE_CHECKING, List, Optional, Set, Tuple, Type, TypeVar, Union
|
||||
|
||||
from blspy import G1Element, G2Element
|
||||
|
@ -105,7 +106,13 @@ class VCWallet:
|
|||
f"Cannot get verified credential coin: {coin.name().hex()} puzzle and solution"
|
||||
) # pragma: no cover
|
||||
return # pragma: no cover
|
||||
vc = VerifiedCredential.get_next_from_coin_spend(cs)
|
||||
try:
|
||||
vc = VerifiedCredential.get_next_from_coin_spend(cs)
|
||||
except Exception as e: # pragma: no cover
|
||||
self.log.debug(
|
||||
f"Syncing VC from coin spend failed (likely means it was revoked): {e}\n{traceback.format_exc()}"
|
||||
)
|
||||
return
|
||||
vc_record: VCRecord = VCRecord(vc, height)
|
||||
self.wallet_state_manager.state_changed(
|
||||
"vc_coin_added", self.id(), dict(launcher_id=vc_record.vc.launcher_id.hex())
|
||||
|
|
|
@ -77,7 +77,7 @@ from chia.wallet.trading.trade_status import TradeStatus
|
|||
from chia.wallet.transaction_record import TransactionRecord
|
||||
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
||||
from chia.wallet.util.address_type import AddressType
|
||||
from chia.wallet.util.compute_hints import compute_coin_hints
|
||||
from chia.wallet.util.compute_hints import compute_spend_hints_and_additions
|
||||
from chia.wallet.util.compute_memos import compute_memos
|
||||
from chia.wallet.util.puzzle_decorator import PuzzleDecoratorManager
|
||||
from chia.wallet.util.query_filter import HashFilter
|
||||
|
@ -681,12 +681,12 @@ class WalletStateManager:
|
|||
# hint
|
||||
# First spend where 1 mojo coin -> Singleton launcher -> NFT -> NFT
|
||||
uncurried_nft = UncurriedNFT.uncurry(uncurried.mod, uncurried.args)
|
||||
if uncurried_nft is not None:
|
||||
if uncurried_nft is not None and coin_state.coin.amount % 2 == 1:
|
||||
return await self.handle_nft(coin_spend, uncurried_nft, parent_coin_state, coin_state)
|
||||
|
||||
# Check if the coin is a DID
|
||||
did_curried_args = match_did_puzzle(uncurried.mod, uncurried.args)
|
||||
if did_curried_args is not None:
|
||||
if did_curried_args is not None and coin_state.coin.amount % 2 == 1:
|
||||
return await self.handle_did(did_curried_args, parent_coin_state, coin_state, coin_spend, peer)
|
||||
|
||||
# Check if the coin is clawback
|
||||
|
@ -865,12 +865,9 @@ class WalletStateManager:
|
|||
"""
|
||||
mod_hash, tail_hash, inner_puzzle = curried_args
|
||||
|
||||
hint_list = compute_coin_hints(coin_spend)
|
||||
derivation_record = None
|
||||
for hint in hint_list:
|
||||
derivation_record = await self.puzzle_store.get_derivation_record_for_puzzle_hash(bytes32(hint))
|
||||
if derivation_record is not None:
|
||||
break
|
||||
hinted_coin = compute_spend_hints_and_additions(coin_spend)[coin_state.coin.name()]
|
||||
assert hinted_coin.hint is not None, f"hint missing for coin {hinted_coin.coin}"
|
||||
derivation_record = await self.puzzle_store.get_derivation_record_for_puzzle_hash(hinted_coin.hint)
|
||||
|
||||
if derivation_record is None:
|
||||
self.log.info(f"Received state for the coin that doesn't belong to us {coin_state}")
|
||||
|
@ -924,13 +921,9 @@ class WalletStateManager:
|
|||
inner_puzzle_hash = p2_puzzle.get_tree_hash()
|
||||
self.log.info(f"parent: {parent_coin_state.coin.name()} inner_puzzle_hash for parent is {inner_puzzle_hash}")
|
||||
|
||||
hint_list = compute_coin_hints(coin_spend)
|
||||
|
||||
derivation_record = None
|
||||
for hint in hint_list:
|
||||
derivation_record = await self.puzzle_store.get_derivation_record_for_puzzle_hash(bytes32(hint))
|
||||
if derivation_record is not None:
|
||||
break
|
||||
hinted_coin = compute_spend_hints_and_additions(coin_spend)[coin_state.coin.name()]
|
||||
assert hinted_coin.hint is not None, f"hint missing for coin {hinted_coin.coin}"
|
||||
derivation_record = await self.puzzle_store.get_derivation_record_for_puzzle_hash(hinted_coin.hint)
|
||||
|
||||
launch_id: bytes32 = bytes32(bytes(singleton_struct.rest().first())[1:])
|
||||
if derivation_record is None:
|
||||
|
|
2
setup.py
2
setup.py
|
@ -7,8 +7,8 @@ from setuptools import find_packages, setup
|
|||
|
||||
dependencies = [
|
||||
"aiofiles==23.1.0", # Async IO for files
|
||||
"blspy==2.0.1", # Signature library
|
||||
"anyio==3.7.0",
|
||||
"blspy==2.0.2", # Signature library
|
||||
"boto3==1.26.161", # AWS S3 for DL s3 plugin
|
||||
"chiavdf==1.0.8", # timelord and vdf verification
|
||||
"chiabip158==1.2", # bip158-style wallet filters
|
||||
|
|
|
@ -17,8 +17,14 @@ from types import TracebackType
|
|||
from typing import Any, Callable, Collection, Iterator, List, Optional, Type, Union
|
||||
|
||||
import pytest
|
||||
from chia_rs import Coin
|
||||
from typing_extensions import Protocol, final
|
||||
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.condition_opcodes import ConditionOpcode
|
||||
from chia.util.hash import std_hash
|
||||
from chia.util.ints import uint64
|
||||
from chia.wallet.util.compute_hints import HintedCoin
|
||||
from tests.core.data_layer.util import ChiaRoot
|
||||
|
||||
|
||||
|
@ -332,3 +338,32 @@ class DataCasesDecorator(Protocol):
|
|||
|
||||
def named_datacases(name: str) -> DataCasesDecorator:
|
||||
return functools.partial(datacases, _name=name)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CoinGenerator:
|
||||
_seed: int = -1
|
||||
|
||||
def _get_hash(self) -> bytes32:
|
||||
self._seed += 1
|
||||
return std_hash(self._seed)
|
||||
|
||||
def _get_amount(self) -> uint64:
|
||||
self._seed += 1
|
||||
return uint64(self._seed)
|
||||
|
||||
def get(self, parent_coin_id: Optional[bytes32] = None, include_hint: bool = True) -> HintedCoin:
|
||||
if parent_coin_id is None:
|
||||
parent_coin_id = self._get_hash()
|
||||
hint = None
|
||||
if include_hint:
|
||||
hint = self._get_hash()
|
||||
return HintedCoin(Coin(parent_coin_id, self._get_hash(), self._get_amount()), hint)
|
||||
|
||||
|
||||
def coin_creation_args(hinted_coin: HintedCoin) -> List[Any]:
|
||||
if hinted_coin.hint is not None:
|
||||
memos = [hinted_coin.hint]
|
||||
else:
|
||||
memos = []
|
||||
return [ConditionOpcode.CREATE_COIN, hinted_coin.coin.puzzle_hash, hinted_coin.coin.amount, memos]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# flake8: noqa: E501
|
||||
from __future__ import annotations
|
||||
|
||||
checkout_blocks_and_plots = True
|
|
@ -0,0 +1,20 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from chia.types.blockchain_format.program import Program
|
||||
from chia.types.coin_spend import CoinSpend
|
||||
from chia.wallet.util.compute_hints import compute_spend_hints_and_additions
|
||||
from tests.util.misc import CoinGenerator, coin_creation_args
|
||||
|
||||
|
||||
def test_compute_spend_hints_and_additions() -> None:
|
||||
coin_generator = CoinGenerator()
|
||||
parent_coin = coin_generator.get()
|
||||
hinted_coins = [coin_generator.get(parent_coin.coin.name(), include_hint=i % 2 == 0) for i in range(10)]
|
||||
create_coin_args = [coin_creation_args(create_coin) for create_coin in hinted_coins]
|
||||
coin_spend = CoinSpend(
|
||||
parent_coin.coin,
|
||||
Program.to(1),
|
||||
Program.to(create_coin_args),
|
||||
)
|
||||
expected_dict = {hinted_coin.coin.name(): hinted_coin for hinted_coin in hinted_coins}
|
||||
assert compute_spend_hints_and_additions(coin_spend) == expected_dict
|
|
@ -13,6 +13,8 @@ import typing
|
|||
import click
|
||||
import typing_extensions
|
||||
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
|
||||
here = pathlib.Path(__file__).parent.resolve()
|
||||
root = here.parent
|
||||
cache_path = root.joinpath(".chia_cache", "manage_clvm.json")
|
||||
|
@ -99,7 +101,7 @@ def dump_cache(cache: Cache, file: typing.IO[str]) -> None:
|
|||
json.dump(cache, file, indent=4)
|
||||
|
||||
|
||||
def generate_hash_bytes(hex_bytes: bytes) -> bytes:
|
||||
def generate_hash_bytes(hex_bytes: bytes) -> bytes32:
|
||||
cleaned_blob = bytes.fromhex(hex_bytes.decode("utf-8"))
|
||||
serialize_program = SerializedProgram.from_bytes(cleaned_blob)
|
||||
return serialize_program.get_tree_hash()
|
||||
|
@ -141,7 +143,7 @@ class ClvmBytes:
|
|||
hex_bytes = paths.hex.read_bytes()
|
||||
return cls(
|
||||
hex=hex_bytes,
|
||||
hash=bytes.fromhex(hash_dict[paths.hash])
|
||||
hash=bytes32(bytes.fromhex(hash_dict[paths.hash]))
|
||||
if paths.hash in hash_dict
|
||||
else generate_hash_bytes(hex_bytes=hex_bytes),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue