Add extra_conditions as an option to transaction endpoints (#15911)

Add extra_conditions as an argument to all transaction methods
This commit is contained in:
Matt Hauff 2023-08-29 11:48:16 -07:00 committed by GitHub
parent 05500ea840
commit 6af4f86171
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 640 additions and 114 deletions

View File

@ -288,6 +288,24 @@ def tx_config_args(func: Callable[..., None]) -> Callable[..., None]:
)(coin_selection_args(func))
def timelock_args(func: Callable[..., None]) -> Callable[..., None]:
return click.option(
"--valid-at",
help="UNIX timestamp at which the associated transactions become valid",
type=int,
required=False,
default=None,
)(
click.option(
"--expires-at",
help="UNIX timestamp at which the associated transactions expire",
type=int,
required=False,
default=None,
)(func)
)
@streamable
@dataclasses.dataclass(frozen=True)
class CMDCoinSelectionConfigLoader(Streamable):

View File

@ -25,7 +25,7 @@ from chia.types.condition_opcodes import ConditionOpcode
from chia.types.spend_bundle import SpendBundle
from chia.util.ints import uint8, uint32, uint64, uint128
from chia.util.streamable import Streamable, streamable
from chia.wallet.conditions import UnknownCondition
from chia.wallet.conditions import Condition, UnknownCondition
from chia.wallet.db_wallet.db_wallet_puzzles import (
ACS_MU,
ACS_MU_PH,
@ -292,6 +292,7 @@ class DataLayerWallet:
initial_root: bytes32,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[TransactionRecord, TransactionRecord, bytes32]:
"""
Creates the initial singleton, which includes spending an origin coin, the launcher, and creating a singleton
@ -322,6 +323,7 @@ class DataLayerWallet:
primaries=None,
ignore_max_send_amount=False,
coin_announcements_to_consume={announcement},
extra_conditions=extra_conditions,
)
assert create_launcher_tx_record is not None and create_launcher_tx_record.spend_bundle is not None
@ -406,6 +408,7 @@ class DataLayerWallet:
sign: bool = True,
add_pending_singleton: bool = True,
announce_new_state: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
singleton_record, parent_lineage = await self.get_spendable_singleton_info(launcher_id)
@ -541,6 +544,17 @@ class DataLayerWallet:
],
)
]
if root_hash != singleton_record.root:
extra_conditions = (
*extra_conditions,
UnknownCondition(
opcode=Program.to(-24),
args=[
ACS_MU,
Program.to([[(root_hash, None), ACS_MU_PH], None]),
],
),
)
inner_sol: Program = self.standard_wallet.make_solution(
primaries=primaries,
coin_announcements={b"$"} if fee > 0 else None,
@ -550,17 +564,7 @@ class DataLayerWallet:
puzzle_announcements_to_assert={a.name() for a in puzzle_announcements_to_consume}
if puzzle_announcements_to_consume is not None
else None,
conditions=[
UnknownCondition(
opcode=Program.to(-24),
args=[
ACS_MU,
Program.to([[(root_hash, None), ACS_MU_PH], None]),
],
)
]
if root_hash != singleton_record.root
else [],
conditions=extra_conditions,
)
db_layer_sol = Program.to([inner_sol])
full_sol = Program.to(
@ -639,6 +643,7 @@ class DataLayerWallet:
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
ignore_max_send_amount: bool = False, # ignored
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> List[TransactionRecord]:
launcher_id: Optional[bytes32] = kwargs.get("launcher_id", None)
@ -677,6 +682,7 @@ class DataLayerWallet:
sign,
add_pending_singleton,
announce_new_state,
extra_conditions,
)
async def get_spendable_singleton_info(self, launcher_id: bytes32) -> Tuple[SingletonRecord, LineageProof]:
@ -734,7 +740,13 @@ class DataLayerWallet:
return collected
async def create_new_mirror(
self, launcher_id: bytes32, amount: uint64, urls: List[bytes], tx_config: TXConfig, fee: uint64 = uint64(0)
self,
launcher_id: bytes32,
amount: uint64,
urls: List[bytes],
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
create_mirror_tx_record: Optional[TransactionRecord] = await self.standard_wallet.generate_signed_transaction(
amount=amount,
@ -744,12 +756,18 @@ class DataLayerWallet:
primaries=[],
memos=[launcher_id, *(url for url in urls)],
ignore_max_send_amount=False,
extra_conditions=extra_conditions,
)
assert create_mirror_tx_record is not None and create_mirror_tx_record.spend_bundle is not None
return [create_mirror_tx_record]
async def delete_mirror(
self, mirror_id: bytes32, peer: WSChiaConnection, tx_config: TXConfig, fee: uint64 = uint64(0)
self,
mirror_id: bytes32,
peer: WSChiaConnection,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
mirror: Mirror = await self.get_mirror(mirror_id)
mirror_coin: Coin = (await self.wallet_state_manager.wallet_node.get_coin_state([mirror.coin_id], peer=peer))[
@ -770,6 +788,7 @@ class DataLayerWallet:
inner_sol: Program = self.standard_wallet.make_solution(
primaries=[Payment(new_puzhash, uint64(mirror_coin.amount - fee))] if excess_fee < 0 else [],
coin_announcements={b"$"} if excess_fee > 0 else None,
conditions=extra_conditions,
)
mirror_spend = CoinSpend(
mirror_coin,

View File

@ -46,6 +46,7 @@ from chia.types.coin_record import CoinRecord
from chia.types.coin_spend import CoinSpend, compute_additions
from chia.types.spend_bundle import SpendBundle
from chia.util.ints import uint32, uint64, uint128
from chia.wallet.conditions import Condition
from chia.wallet.derive_keys import find_owner_sk
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_synthetic_public_key
from chia.wallet.sign_coin_spends import sign_coin_spends
@ -398,6 +399,7 @@ class PoolWallet:
fee: uint64 = uint64(0),
p2_singleton_delay_time: Optional[uint64] = None,
p2_singleton_delayed_ph: Optional[bytes32] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[TransactionRecord, bytes32, bytes32]:
"""
A "plot NFT", or pool wallet, represents the idea of a set of plots that all pay to
@ -435,6 +437,7 @@ class PoolWallet:
p2_singleton_delay_time,
p2_singleton_delayed_ph,
tx_config,
extra_conditions=extra_conditions,
)
if spend_bundle is None:
@ -644,6 +647,7 @@ class PoolWallet:
delay_time: uint64,
delay_ph: bytes32,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[SpendBundle, bytes32, bytes32]:
"""
Creates the initial singleton, which includes spending an origin coin, the launcher, and creating a singleton
@ -702,6 +706,7 @@ class PoolWallet:
False,
announcement_set,
origin_id=launcher_parent.name(),
extra_conditions=extra_conditions,
)
assert create_launcher_tx_record is not None and create_launcher_tx_record.spend_bundle is not None

View File

@ -2,12 +2,13 @@ from __future__ import annotations
import logging
import traceback
from typing import Any, Callable, Coroutine, Dict, List, Optional
from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple
import aiohttp
from chia.types.blockchain_format.coin import Coin
from chia.util.json_util import obj_to_response
from chia.wallet.conditions import Condition, conditions_from_json_dicts
from chia.wallet.util.tx_config import TXConfig, TXConfigLoader
log = logging.getLogger(__name__)
@ -63,6 +64,11 @@ def tx_endpoint(
config=self.service.wallet_state_manager.config,
logged_in_fingerprint=self.service.logged_in_fingerprint,
)
return await func(self, request, *args, tx_config=tx_config, **kwargs)
extra_conditions: Tuple[Condition, ...] = tuple()
if "extra_conditions" in request:
extra_conditions = tuple(conditions_from_json_dicts(request["extra_conditions"]))
return await func(self, request, *args, tx_config=tx_config, extra_conditions=extra_conditions, **kwargs)
return rpc_endpoint

View File

@ -43,6 +43,7 @@ from chia.util.ws_message import WsRpcMessage, create_payload_dict
from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
from chia.wallet.cat_wallet.cat_info import CRCATInfo
from chia.wallet.cat_wallet.cat_wallet import CATWallet
from chia.wallet.conditions import Condition
from chia.wallet.derive_keys import (
MAX_POOL_WALLETS,
master_sk_to_farmer_sk,
@ -630,7 +631,10 @@ class WalletRpcApi:
@tx_endpoint
async def create_new_wallet(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_state_manager = self.service.wallet_state_manager
@ -819,6 +823,7 @@ class WalletRpcApi:
fee,
request.get("p2_singleton_delay_time", None),
delayed_address,
extra_conditions=extra_conditions,
)
except Exception as e:
raise ValueError(str(e))
@ -1008,7 +1013,10 @@ class WalletRpcApi:
@tx_endpoint
async def send_transaction(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
if await self.service.wallet_state_manager.synced() is False:
raise ValueError("Wallet needs to be fully synced before sending transactions")
@ -1040,6 +1048,7 @@ class WalletRpcApi:
fee,
memos=memos,
puzzle_decorator_override=request.get("puzzle_decorator", None),
extra_conditions=extra_conditions,
)
await wallet.push_transaction(tx)
@ -1072,7 +1081,10 @@ class WalletRpcApi:
@tx_endpoint
async def spend_clawback_coins(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Spend clawback coins that were sent (to claw them back) or received (to claim them).
@ -1107,7 +1119,7 @@ class WalletRpcApi:
tx_id_list.extend(
(
await self.service.wallet_state_manager.spend_clawback_coins(
coins, tx_fee, tx_config, request.get("force", False)
coins, tx_fee, tx_config, request.get("force", False), extra_conditions=extra_conditions
)
)
)
@ -1118,7 +1130,7 @@ class WalletRpcApi:
tx_id_list.extend(
(
await self.service.wallet_state_manager.spend_clawback_coins(
coins, tx_fee, tx_config, request.get("force", False)
coins, tx_fee, tx_config, request.get("force", False), extra_conditions=extra_conditions
)
)
)
@ -1143,7 +1155,12 @@ class WalletRpcApi:
return {}
@tx_endpoint
async def select_coins(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def select_coins(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
if await self.service.wallet_state_manager.synced() is False:
raise ValueError("Wallet needs to be fully synced before selecting coins")
@ -1355,7 +1372,10 @@ class WalletRpcApi:
@tx_endpoint
async def send_notification(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
tx: TransactionRecord = await self.service.wallet_state_manager.notification_manager.send_new_notification(
bytes32.from_hexstr(request["target"]),
@ -1363,6 +1383,7 @@ class WalletRpcApi:
uint64(request["amount"]),
tx_config,
request.get("fee", uint64(0)),
extra_conditions=extra_conditions,
)
await self.service.wallet_state_manager.add_pending_transaction(tx)
return {"tx": tx.to_json_dict_convenience(self.service.config)}
@ -1519,7 +1540,11 @@ class WalletRpcApi:
@tx_endpoint
async def cat_spend(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG, hold_lock: bool = True
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
hold_lock: bool = True,
) -> EndpointResult:
if await self.service.wallet_state_manager.synced() is False:
raise ValueError("Wallet needs to be fully synced.")
@ -1582,6 +1607,7 @@ class WalletRpcApi:
cat_discrepancy=cat_discrepancy,
coins=coins,
memos=memos if memos else None,
extra_conditions=extra_conditions,
)
for tx in txs:
await wallet.standard_wallet.push_transaction(tx)
@ -1594,6 +1620,7 @@ class WalletRpcApi:
cat_discrepancy=cat_discrepancy,
coins=coins,
memos=memos if memos else None,
extra_conditions=extra_conditions,
)
for tx in txs:
await wallet.standard_wallet.push_transaction(tx)
@ -1623,7 +1650,10 @@ class WalletRpcApi:
@tx_endpoint
async def create_offer_for_ids(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
offer: Dict[str, int] = request["offer"]
fee: uint64 = uint64(request.get("fee", 0))
@ -1666,6 +1696,7 @@ class WalletRpcApi:
solver=solver,
fee=fee,
validate_only=validate_only,
extra_conditions=extra_conditions,
)
if result[0]:
success, trade_record, error = result
@ -1766,7 +1797,12 @@ class WalletRpcApi:
}
@tx_endpoint
async def take_offer(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def take_offer(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
offer_hex: str = request["offer"]
###
@ -1805,6 +1841,7 @@ class WalletRpcApi:
tx_config,
fee=fee,
solver=solver,
extra_conditions=extra_conditions,
)
return {"trade_record": trade_record.to_json_dict_convenience()}
@ -1860,17 +1897,29 @@ class WalletRpcApi:
return {"total": total, "my_offers_count": my_offers_count, "taken_offers_count": taken_offers_count}
@tx_endpoint
async def cancel_offer(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def cancel_offer(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wsm = self.service.wallet_state_manager
secure = request["secure"]
trade_id = bytes32.from_hexstr(request["trade_id"])
fee: uint64 = uint64(request.get("fee", 0))
async with self.service.wallet_state_manager.lock:
await wsm.trade_manager.cancel_pending_offers([bytes32(trade_id)], tx_config, fee=fee, secure=secure)
await wsm.trade_manager.cancel_pending_offers(
[bytes32(trade_id)], tx_config, fee=fee, secure=secure, extra_conditions=extra_conditions
)
return {}
@tx_endpoint
async def cancel_offers(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def cancel_offers(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
secure = request["secure"]
batch_fee: uint64 = uint64(request.get("batch_fee", 0))
batch_size = request.get("batch_size", 5)
@ -1909,7 +1958,9 @@ class WalletRpcApi:
continue
async with self.service.wallet_state_manager.lock:
await trade_mgr.cancel_pending_offers(list(records.keys()), tx_config, batch_fee, secure, records)
await trade_mgr.cancel_pending_offers(
list(records.keys()), tx_config, batch_fee, secure, records, extra_conditions=extra_conditions
)
log.info(f"Cancelled offers {start} to {end} ...")
# If fewer records were returned than requested, we're done
if len(trades) < batch_size:
@ -1936,7 +1987,10 @@ class WalletRpcApi:
@tx_endpoint
async def did_update_recovery_ids(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@ -1952,14 +2006,19 @@ class WalletRpcApi:
update_success = await wallet.update_recovery_list(recovery_list, new_amount_verifications_required)
# Update coin with new ID info
if update_success:
spend_bundle = await wallet.create_update_spend(tx_config, fee=uint64(request.get("fee", 0)))
spend_bundle = await wallet.create_update_spend(
tx_config, fee=uint64(request.get("fee", 0)), extra_conditions=extra_conditions
)
if spend_bundle is not None:
success = True
return {"success": success}
@tx_endpoint
async def did_message_spend(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@ -1970,7 +2029,9 @@ class WalletRpcApi:
for pa in request.get("puzzle_announcements", []):
puzzle_announcements.add(bytes.fromhex(pa))
spend_bundle = await wallet.create_message_spend(tx_config, coin_announcements, puzzle_announcements)
spend_bundle = await wallet.create_message_spend(
tx_config, coin_announcements, puzzle_announcements, extra_conditions=extra_conditions
)
return {"success": True, "spend_bundle": spend_bundle}
async def did_get_info(self, request: Dict[str, Any]) -> EndpointResult:
@ -2202,7 +2263,10 @@ class WalletRpcApi:
@tx_endpoint
async def did_update_metadata(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@ -2213,7 +2277,9 @@ class WalletRpcApi:
update_success = await wallet.update_metadata(metadata)
# Update coin with new ID info
if update_success:
spend_bundle = await wallet.create_update_spend(tx_config, uint64(request.get("fee", 0)))
spend_bundle = await wallet.create_update_spend(
tx_config, uint64(request.get("fee", 0)), extra_conditions=extra_conditions
)
if spend_bundle is not None:
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
else:
@ -2301,7 +2367,10 @@ class WalletRpcApi:
@tx_endpoint
async def did_create_attest(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
@ -2314,6 +2383,7 @@ class WalletRpcApi:
bytes32.from_hexstr(request["puzhash"]),
pubkey,
tx_config,
extra_conditions=extra_conditions,
)
if info is not None and spend_bundle is not None:
return {
@ -2369,7 +2439,10 @@ class WalletRpcApi:
@tx_endpoint
async def did_transfer_did(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
if await self.service.wallet_state_manager.synced() is False:
raise ValueError("Wallet needs to be fully synced.")
@ -2382,6 +2455,7 @@ class WalletRpcApi:
uint64(request.get("fee", 0)),
request.get("with_recovery_info", True),
tx_config,
extra_conditions=extra_conditions,
)
return {
@ -2394,7 +2468,12 @@ class WalletRpcApi:
# NFT Wallet
##########################################################################################
@tx_endpoint
async def nft_mint_nft(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def nft_mint_nft(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
log.debug("Got minting RPC request: %s", request)
wallet_id = uint32(request["wallet_id"])
assert self.service.wallet_state_manager
@ -2453,6 +2532,7 @@ class WalletRpcApi:
royalty_amount,
did_id,
fee,
extra_conditions=extra_conditions,
)
nft_id = None
assert spend_bundle is not None
@ -2505,7 +2585,12 @@ class WalletRpcApi:
return {"wallet_id": wallet_id, "success": True, "nft_list": nft_info_list}
@tx_endpoint
async def nft_set_nft_did(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def nft_set_nft_did(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
did_id = request.get("did_id", b"")
@ -2518,12 +2603,21 @@ class WalletRpcApi:
return {"success": False, "error": "The NFT doesn't support setting a DID."}
fee = uint64(request.get("fee", 0))
spend_bundle = await nft_wallet.set_nft_did(nft_coin_info, did_id, tx_config, fee=fee)
spend_bundle = await nft_wallet.set_nft_did(
nft_coin_info,
did_id,
tx_config,
fee=fee,
extra_conditions=extra_conditions,
)
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
@tx_endpoint
async def nft_set_did_bulk(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Bulk set DID for NFTs across different wallets.
@ -2574,9 +2668,15 @@ class WalletRpcApi:
for wallet_id, nft_list in nft_dict.items():
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
if not first:
tx_list.extend(await nft_wallet.set_bulk_nft_did(nft_list, did_id, tx_config))
tx_list.extend(
await nft_wallet.set_bulk_nft_did(nft_list, did_id, tx_config, extra_conditions=extra_conditions)
)
else:
tx_list.extend(await nft_wallet.set_bulk_nft_did(nft_list, did_id, tx_config, fee, nft_ids))
tx_list.extend(
await nft_wallet.set_bulk_nft_did(
nft_list, did_id, tx_config, fee, nft_ids, extra_conditions=extra_conditions
)
)
for coin in nft_list:
coin_ids.append(coin.coin.name())
first = False
@ -2609,7 +2709,10 @@ class WalletRpcApi:
@tx_endpoint
async def nft_transfer_bulk(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Bulk transfer NFTs to an address.
@ -2655,9 +2758,17 @@ class WalletRpcApi:
for wallet_id, nft_list in nft_dict.items():
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
if not first:
tx_list.extend(await nft_wallet.bulk_transfer_nft(nft_list, puzzle_hash, tx_config))
tx_list.extend(
await nft_wallet.bulk_transfer_nft(
nft_list, puzzle_hash, tx_config, extra_conditions=extra_conditions
)
)
else:
tx_list.extend(await nft_wallet.bulk_transfer_nft(nft_list, puzzle_hash, tx_config, fee))
tx_list.extend(
await nft_wallet.bulk_transfer_nft(
nft_list, puzzle_hash, tx_config, fee, extra_conditions=extra_conditions
)
)
for coin in nft_list:
coin_ids.append(coin.coin.name())
first = False
@ -2744,7 +2855,10 @@ class WalletRpcApi:
@tx_endpoint
async def nft_transfer_nft(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
address = request["target_address"]
@ -2772,6 +2886,7 @@ class WalletRpcApi:
fee=fee,
new_owner=b"",
new_did_inner_hash=b"",
extra_conditions=extra_conditions,
)
spend_bundle: Optional[SpendBundle] = None
for tx in txs:
@ -2852,7 +2967,12 @@ class WalletRpcApi:
return {"success": True, "nft_info": nft_info}
@tx_endpoint
async def nft_add_uri(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def nft_add_uri(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
# Note metadata updater can only add one uri for one field per spend.
# If you want to add multiple uris for one field, you need to spend multiple times.
@ -2867,7 +2987,9 @@ class WalletRpcApi:
nft_coin_info = await nft_wallet.get_nft_coin_by_id(nft_coin_id)
fee = uint64(request.get("fee", 0))
spend_bundle = await nft_wallet.update_metadata(nft_coin_info, key, uri, tx_config, fee=fee)
spend_bundle = await nft_wallet.update_metadata(
nft_coin_info, key, uri, tx_config, fee=fee, extra_conditions=extra_conditions
)
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
async def nft_calculate_royalties(self, request: Dict[str, Any]) -> EndpointResult:
@ -2880,7 +3002,12 @@ class WalletRpcApi:
)
@tx_endpoint
async def nft_mint_bulk(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def nft_mint_bulk(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
if await self.service.wallet_state_manager.synced() is False:
raise ValueError("Wallet needs to be fully synced.")
wallet_id = uint32(request["wallet_id"])
@ -2974,6 +3101,7 @@ class WalletRpcApi:
did_lineage_parent=did_lineage_parent,
fee=fee,
tx_config=tx_config,
extra_conditions=extra_conditions,
)
else:
sb = await nft_wallet.mint_from_xch(
@ -2985,6 +3113,7 @@ class WalletRpcApi:
xch_change_ph=xch_change_ph,
fee=fee,
tx_config=tx_config,
extra_conditions=extra_conditions,
)
nft_id_list = []
for cs in sb.coin_spends:
@ -3085,7 +3214,11 @@ class WalletRpcApi:
@tx_endpoint
async def create_signed_transaction(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG, hold_lock: bool = True
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
hold_lock: bool = True,
) -> EndpointResult:
if "wallet_id" in request:
wallet_id = uint32(request["wallet_id"])
@ -3173,6 +3306,7 @@ class WalletRpcApi:
memos=memos_0,
coin_announcements_to_consume=coin_announcements,
puzzle_announcements_to_consume=puzzle_announcements,
extra_conditions=extra_conditions,
)
signed_tx = tx.to_json_dict_convenience(self.service.config)
@ -3191,6 +3325,7 @@ class WalletRpcApi:
memos=[memos_0] + [output.memos for output in additional_outputs],
coin_announcements_to_consume=coin_announcements,
puzzle_announcements_to_consume=puzzle_announcements,
extra_conditions=extra_conditions,
)
signed_txs = [tx.to_json_dict_convenience(self.service.config) for tx in txs]
@ -3206,7 +3341,12 @@ class WalletRpcApi:
# Pool Wallet
##########################################################################################
@tx_endpoint
async def pw_join_pool(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def pw_join_pool(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
fee = uint64(request.get("fee", 0))
wallet_id = uint32(request["wallet_id"])
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
@ -3234,7 +3374,12 @@ class WalletRpcApi:
return {"total_fee": total_fee, "transaction": tx, "fee_transaction": fee_tx}
@tx_endpoint
async def pw_self_pool(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def pw_self_pool(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
# Leaving a pool requires two state transitions.
# First we transition to PoolSingletonState.LEAVING_POOL
# Then we transition to FARMING_TO_POOL or SELF_POOLING
@ -3251,7 +3396,10 @@ class WalletRpcApi:
@tx_endpoint
async def pw_absorb_rewards(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Perform a sweep of the p2_singleton rewards controlled by the pool wallet singleton"""
if await self.service.wallet_state_manager.synced() is False:
@ -3284,7 +3432,12 @@ class WalletRpcApi:
# DataLayer Wallet
##########################################################################################
@tx_endpoint
async def create_new_dl(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def create_new_dl(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Initialize the DataLayer Wallet (only one can exist)"""
if self.service.wallet_state_manager is None:
raise ValueError("The wallet service is not currently initialized")
@ -3298,7 +3451,10 @@ class WalletRpcApi:
try:
async with self.service.wallet_state_manager.lock:
dl_tx, std_tx, launcher_id = await dl_wallet.generate_new_reporter(
bytes32.from_hexstr(request["root"]), tx_config, fee=request.get("fee", uint64(0))
bytes32.from_hexstr(request["root"]),
tx_config,
fee=request.get("fee", uint64(0)),
extra_conditions=extra_conditions,
)
await self.service.wallet_state_manager.add_pending_transaction(dl_tx)
await self.service.wallet_state_manager.add_pending_transaction(std_tx)
@ -3371,7 +3527,12 @@ class WalletRpcApi:
return {"singletons": records_json}
@tx_endpoint
async def dl_update_root(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def dl_update_root(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Get the singleton record for the latest singleton of a launcher ID"""
if self.service.wallet_state_manager is None:
raise ValueError("The wallet service is not currently initialized")
@ -3383,6 +3544,7 @@ class WalletRpcApi:
bytes32.from_hexstr(request["new_root"]),
tx_config,
fee=uint64(request.get("fee", 0)),
extra_conditions=extra_conditions,
)
for record in records:
await self.service.wallet_state_manager.add_pending_transaction(record)
@ -3390,7 +3552,10 @@ class WalletRpcApi:
@tx_endpoint
async def dl_update_multiple(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Update multiple singletons with new merkle roots"""
if self.service.wallet_state_manager is None:
@ -3403,7 +3568,10 @@ class WalletRpcApi:
tx_records: List[TransactionRecord] = []
for launcher, root in request["updates"].items():
records = await wallet.create_update_state_spend(
bytes32.from_hexstr(launcher), bytes32.from_hexstr(root), tx_config
bytes32.from_hexstr(launcher),
bytes32.from_hexstr(root),
tx_config,
extra_conditions=extra_conditions,
)
tx_records.extend(records)
# Now that we have all the txs, we need to aggregate them all into just one spend
@ -3461,7 +3629,12 @@ class WalletRpcApi:
return {"mirrors": mirrors_json}
@tx_endpoint
async def dl_new_mirror(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def dl_new_mirror(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Add a new on chain message for a specific singleton"""
if self.service.wallet_state_manager is None:
raise ValueError("The wallet service is not currently initialized")
@ -3474,6 +3647,7 @@ class WalletRpcApi:
[bytes(url, "utf8") for url in request["urls"]],
tx_config,
fee=request.get("fee", uint64(0)),
extra_conditions=extra_conditions,
)
for tx in txs:
await self.service.wallet_state_manager.add_pending_transaction(tx)
@ -3484,7 +3658,10 @@ class WalletRpcApi:
@tx_endpoint
async def dl_delete_mirror(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""Remove an existing mirror for a specific singleton"""
if self.service.wallet_state_manager is None:
@ -3498,6 +3675,7 @@ class WalletRpcApi:
self.service.get_full_node_peer(),
tx_config,
fee=request.get("fee", uint64(0)),
extra_conditions=extra_conditions,
)
for tx in txs:
await self.service.wallet_state_manager.add_pending_transaction(tx)
@ -3510,7 +3688,12 @@ class WalletRpcApi:
# Verified Credential
##########################################################################################
@tx_endpoint
async def vc_mint(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def vc_mint(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Mint a verified credential using the assigned DID
:param request: We require 'did_id' that will be minting the VC and options for a new 'target_address' as well
@ -3534,7 +3717,9 @@ class WalletRpcApi:
puzhash = decode_puzzle_hash(parsed_request.target_address)
vc_wallet: VCWallet = await self.service.wallet_state_manager.get_or_create_vc_wallet()
vc_record, tx_list = await vc_wallet.launch_new_vc(did_id, tx_config, puzhash, parsed_request.fee)
vc_record, tx_list = await vc_wallet.launch_new_vc(
did_id, tx_config, puzhash, parsed_request.fee, extra_conditions=extra_conditions
)
for tx in tx_list:
await self.service.wallet_state_manager.add_pending_transaction(tx)
return {
@ -3590,7 +3775,12 @@ class WalletRpcApi:
}
@tx_endpoint
async def vc_spend(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def vc_spend(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Spend a verified credential
:param request: Required 'vc_id' launcher id of the vc we wish to spend. Optional paramaters for a 'new_puzhash'
@ -3619,6 +3809,7 @@ class WalletRpcApi:
parsed_request.new_puzhash,
new_proof_hash=parsed_request.new_proof_hash,
provider_inner_puzhash=parsed_request.provider_inner_puzhash,
extra_conditions=extra_conditions,
)
for tx in txs:
await self.service.wallet_state_manager.add_pending_transaction(tx)
@ -3661,7 +3852,12 @@ class WalletRpcApi:
return {"proofs": vc_proofs.key_value_pairs}
@tx_endpoint
async def vc_revoke(self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG) -> EndpointResult:
async def vc_revoke(
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Revoke an on chain VC provided the correct DID is available
:param request: required 'vc_parent_id' for the VC coin. Standard transaction params 'fee' & 'reuse_puzhash'.
@ -3682,6 +3878,7 @@ class WalletRpcApi:
self.service.get_full_node_peer(),
tx_config,
parsed_request.fee,
extra_conditions=extra_conditions,
)
for tx in txs:
await self.service.wallet_state_manager.add_pending_transaction(tx)
@ -3692,7 +3889,10 @@ class WalletRpcApi:
@tx_endpoint
async def crcat_approve_pending(
self, request: Dict[str, Any], tx_config: TXConfig = DEFAULT_TX_CONFIG
self,
request: Dict[str, Any],
tx_config: TXConfig = DEFAULT_TX_CONFIG,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> EndpointResult:
"""
Moving any "pending approval" CR-CATs into the spendable balance of the wallet

View File

@ -12,6 +12,7 @@ from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.coin_record import CoinRecord
from chia.util.bech32m import encode_puzzle_hash
from chia.util.ints import uint16, uint32, uint64
from chia.wallet.conditions import Condition
from chia.wallet.notification_store import Notification
from chia.wallet.trade_record import TradeRecord
from chia.wallet.trading.offer import Offer
@ -189,6 +190,7 @@ class WalletRpcClient(RpcClient):
fee: uint64 = uint64(0),
memos: Optional[List[str]] = None,
puzzle_decorator_override: Optional[List[Dict[str, Union[str, int, bool]]]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
if memos is None:
send_dict: Dict = {
@ -197,6 +199,7 @@ class WalletRpcClient(RpcClient):
"address": address,
"fee": fee,
"puzzle_decorator": puzzle_decorator_override,
"extra_conditions": list(extra_conditions),
}
else:
send_dict = {
@ -206,6 +209,7 @@ class WalletRpcClient(RpcClient):
"fee": fee,
"memos": memos,
"puzzle_decorator": puzzle_decorator_override,
"extra_conditions": list(extra_conditions),
}
send_dict.update(tx_config.to_json_dict())
res = await self.fetch("send_transaction", send_dict)
@ -250,10 +254,16 @@ class WalletRpcClient(RpcClient):
coin_ids: List[bytes32],
fee: int = 0,
force: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
response = await self.fetch(
"spend_clawback_coins",
{"coin_ids": [cid.hex() for cid in coin_ids], "fee": fee, "force": force},
{
"coin_ids": [cid.hex() for cid in coin_ids],
"fee": fee,
"force": force,
"extra_conditions": list(extra_conditions),
},
)
return response
@ -282,6 +292,7 @@ class WalletRpcClient(RpcClient):
coin_announcements: Optional[List[Announcement]] = None,
puzzle_announcements: Optional[List[Announcement]] = None,
wallet_id: Optional[int] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
# Converts bytes to hex for puzzle hashes
additions_hex = []
@ -293,6 +304,7 @@ class WalletRpcClient(RpcClient):
request: Dict[str, Any] = {
"additions": additions_hex,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
@ -452,11 +464,13 @@ class WalletRpcClient(RpcClient):
recovery_list: List[str],
num_verification: int,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"new_list": recovery_list,
"num_verifications_required": num_verification,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("did_update_recovery_ids", request)
@ -470,12 +484,18 @@ class WalletRpcClient(RpcClient):
return response
async def did_message_spend(
self, wallet_id: int, puzzle_announcements: List[str], coin_announcements: List[str], tx_config: TXConfig
self,
wallet_id: int,
puzzle_announcements: List[str],
coin_announcements: List[str],
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"coin_announcements": coin_announcements,
"puzzle_announcements": puzzle_announcements,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("did_message_spend", request)
@ -486,10 +506,12 @@ class WalletRpcClient(RpcClient):
wallet_id: int,
metadata: Dict,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"metadata": metadata,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("did_update_metadata", request)
@ -527,7 +549,13 @@ class WalletRpcClient(RpcClient):
return response
async def did_create_attest(
self, wallet_id: int, coin_name: str, pubkey: str, puzhash: str, file_name: str
self,
wallet_id: int,
coin_name: str,
pubkey: str,
puzhash: str,
file_name: str,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request: Dict[str, Any] = {
"wallet_id": wallet_id,
@ -535,6 +563,7 @@ class WalletRpcClient(RpcClient):
"pubkey": pubkey,
"puzhash": puzhash,
"filename": file_name,
"extra_conditions": list(extra_conditions),
}
response = await self.fetch("did_create_attest", request)
return response
@ -554,12 +583,14 @@ class WalletRpcClient(RpcClient):
fee: int,
with_recovery: bool,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"inner_address": address,
"fee": fee,
"with_recovery_info": with_recovery,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("did_transfer_did", request)
@ -587,6 +618,7 @@ class WalletRpcClient(RpcClient):
fee: uint64,
p2_singleton_delay_time: Optional[uint64] = None,
p2_singleton_delayed_ph: Optional[bytes32] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
request: Dict[str, Any] = {
"wallet_type": "pool_wallet",
@ -598,6 +630,7 @@ class WalletRpcClient(RpcClient):
"state": state,
},
"fee": fee,
"extra_conditions": list(extra_conditions),
}
if p2_singleton_delay_time is not None:
request["p2_singleton_delay_time"] = p2_singleton_delay_time
@ -707,11 +740,13 @@ class WalletRpcClient(RpcClient):
additions: Optional[List[Dict[str, Any]]] = None,
removals: Optional[List[Coin]] = None,
cat_discrepancy: Optional[Tuple[int, Program, Program]] = None, # (extra_delta, tail_reveal, tail_solution)
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
send_dict: Dict[str, Any] = {
"wallet_id": wallet_id,
"fee": fee,
"memos": memos if memos else [],
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
if amount is not None and inner_address is not None:
@ -744,6 +779,7 @@ class WalletRpcClient(RpcClient):
solver: Dict[str, Any] = None,
fee=uint64(0),
validate_only: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[Optional[Offer], TradeRecord]:
send_dict: Dict[str, int] = {str(key): value for key, value in offer_dict.items()}
@ -751,6 +787,7 @@ class WalletRpcClient(RpcClient):
"offer": send_dict,
"validate_only": validate_only,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
if driver_dict is not None:
@ -778,10 +815,12 @@ class WalletRpcClient(RpcClient):
tx_config: TXConfig,
solver: Dict[str, Any] = None,
fee=uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TradeRecord:
req = {
"offer": offer.to_bech32(),
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
if solver is not None:
@ -829,9 +868,23 @@ class WalletRpcClient(RpcClient):
return records
async def cancel_offer(self, trade_id: bytes32, tx_config: TXConfig, fee=uint64(0), secure: bool = True):
async def cancel_offer(
self,
trade_id: bytes32,
tx_config: TXConfig,
fee=uint64(0),
secure: bool = True,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
await self.fetch(
"cancel_offer", {"trade_id": trade_id.hex(), "secure": secure, "fee": fee, **tx_config.to_json_dict()}
"cancel_offer",
{
"trade_id": trade_id.hex(),
"secure": secure,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
},
)
async def cancel_offers(
@ -842,6 +895,7 @@ class WalletRpcClient(RpcClient):
batch_size: int = 5,
cancel_all: bool = False,
asset_id: Optional[bytes32] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> None:
await self.fetch(
"cancel_offers",
@ -852,6 +906,7 @@ class WalletRpcClient(RpcClient):
"batch_size": batch_size,
"cancel_all": cancel_all,
"asset_id": None if asset_id is None else asset_id.hex(),
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
},
)
@ -883,6 +938,7 @@ class WalletRpcClient(RpcClient):
fee=0,
royalty_percentage=0,
did_id=None,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
@ -899,6 +955,7 @@ class WalletRpcClient(RpcClient):
"royalty_percentage": royalty_percentage,
"did_id": did_id,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("nft_mint_nft", request)
@ -912,6 +969,7 @@ class WalletRpcClient(RpcClient):
uri,
fee,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
@ -919,6 +977,7 @@ class WalletRpcClient(RpcClient):
"uri": uri,
"key": key,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("nft_add_uri", request)
@ -952,12 +1011,14 @@ class WalletRpcClient(RpcClient):
target_address,
fee,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"nft_coin_id": nft_coin_id,
"target_address": target_address,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("nft_transfer_nft", request)
@ -980,12 +1041,14 @@ class WalletRpcClient(RpcClient):
nft_coin_id,
fee,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"did_id": did_id,
"nft_coin_id": nft_coin_id,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("nft_set_nft_did", request)
@ -1013,6 +1076,7 @@ class WalletRpcClient(RpcClient):
did_lineage_parent: Optional[str] = None,
mint_from_did: Optional[bool] = False,
fee: Optional[int] = 0,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Dict:
request = {
"wallet_id": wallet_id,
@ -1029,14 +1093,24 @@ class WalletRpcClient(RpcClient):
"did_lineage_parent": did_lineage_parent,
"mint_from_did": mint_from_did,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
}
response = await self.fetch("nft_mint_bulk", request)
return response
# DataLayer
async def create_new_dl(self, root: bytes32, fee: uint64) -> Tuple[List[TransactionRecord], bytes32]:
request = {"root": root.hex(), "fee": fee}
async def create_new_dl(
self,
root: bytes32,
fee: uint64,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[List[TransactionRecord], bytes32]:
request = {
"root": root.hex(),
"fee": fee,
"extra_conditions": list(extra_conditions),
}
response = await self.fetch("create_new_dl", request)
txs: List[TransactionRecord] = [
TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]
@ -1066,16 +1140,31 @@ class WalletRpcClient(RpcClient):
response = await self.fetch("dl_singletons_by_root", request)
return [SingletonRecord.from_json_dict(single) for single in response["singletons"]]
async def dl_update_root(self, launcher_id: bytes32, new_root: bytes32, fee: uint64) -> TransactionRecord:
request = {"launcher_id": launcher_id.hex(), "new_root": new_root.hex(), "fee": fee}
async def dl_update_root(
self,
launcher_id: bytes32,
new_root: bytes32,
fee: uint64,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
request = {
"launcher_id": launcher_id.hex(),
"new_root": new_root.hex(),
"fee": fee,
"extra_conditions": list(extra_conditions),
}
response = await self.fetch("dl_update_root", request)
return TransactionRecord.from_json_dict_convenience(response["tx_record"])
async def dl_update_multiple(self, update_dictionary: Dict[bytes32, bytes32]) -> List[TransactionRecord]:
async def dl_update_multiple(
self,
update_dictionary: Dict[bytes32, bytes32],
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
updates_as_strings: Dict[str, str] = {}
for lid, root in update_dictionary.items():
updates_as_strings[str(lid)] = str(root)
request = {"updates": updates_as_strings}
request = {"updates": updates_as_strings, "extra_conditions": list(extra_conditions)}
response = await self.fetch("dl_update_multiple", request)
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["tx_records"]]
@ -1107,7 +1196,12 @@ class WalletRpcClient(RpcClient):
return [Mirror.from_json_dict(mirror) for mirror in response["mirrors"]]
async def dl_new_mirror(
self, launcher_id: bytes32, amount: uint64, urls: List[bytes], fee: uint64 = uint64(0)
self,
launcher_id: bytes32,
amount: uint64,
urls: List[bytes],
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
response = await self.fetch(
path="dl_new_mirror",
@ -1116,16 +1210,23 @@ class WalletRpcClient(RpcClient):
"amount": amount,
"urls": [url.decode("utf8") for url in urls],
"fee": fee,
"extra_conditions": list(extra_conditions),
},
)
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]]
async def dl_delete_mirror(self, coin_id: bytes32, fee: uint64 = uint64(0)) -> List[TransactionRecord]:
async def dl_delete_mirror(
self,
coin_id: bytes32,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
response = await self.fetch(
path="dl_delete_mirror",
request_json={
"coin_id": coin_id.hex(),
"fee": fee,
"extra_conditions": list(extra_conditions),
},
)
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]]
@ -1160,7 +1261,12 @@ class WalletRpcClient(RpcClient):
return response["success"]
async def send_notification(
self, target: bytes32, msg: bytes, amount: uint64, fee: uint64 = uint64(0)
self,
target: bytes32,
msg: bytes,
amount: uint64,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
response = await self.fetch(
"send_notification",
@ -1169,6 +1275,7 @@ class WalletRpcClient(RpcClient):
"message": msg.hex(),
"amount": amount,
"fee": fee,
"extra_conditions": list(extra_conditions),
},
)
return TransactionRecord.from_json_dict_convenience(response["tx"])
@ -1182,7 +1289,12 @@ class WalletRpcClient(RpcClient):
return response["pubkey"], response["signature"], response["signing_mode"]
async def vc_mint(
self, did_id: bytes32, tx_config: TXConfig, target_address: Optional[bytes32] = None, fee: uint64 = uint64(0)
self,
did_id: bytes32,
tx_config: TXConfig,
target_address: Optional[bytes32] = None,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[VCRecord, List[TransactionRecord]]:
response = await self.fetch(
"vc_mint",
@ -1190,6 +1302,7 @@ class WalletRpcClient(RpcClient):
"did_id": encode_puzzle_hash(did_id, "rpc"),
"target_address": encode_puzzle_hash(target_address, "rpc") if target_address is not None else None,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
},
)
@ -1213,6 +1326,7 @@ class WalletRpcClient(RpcClient):
new_proof_hash: Optional[bytes32] = None,
provider_inner_puzhash: Optional[bytes32] = None,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
response = await self.fetch(
"vc_spend",
@ -1224,6 +1338,7 @@ class WalletRpcClient(RpcClient):
if provider_inner_puzhash is not None
else provider_inner_puzhash,
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
},
)
@ -1241,9 +1356,16 @@ class WalletRpcClient(RpcClient):
vc_parent_id: bytes32,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
response = await self.fetch(
"vc_revoke", {"vc_parent_id": vc_parent_id.hex(), "fee": fee, **tx_config.to_json_dict()}
"vc_revoke",
{
"vc_parent_id": vc_parent_id.hex(),
"fee": fee,
"extra_conditions": list(extra_conditions),
**tx_config.to_json_dict(),
},
)
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]]

View File

@ -618,6 +618,7 @@ class CATWallet:
coins: Optional[Set[Coin]] = None,
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[SpendBundle, Optional[TransactionRecord]]:
if coin_announcements_to_consume is not None:
coin_announcements_bytes: Optional[Set[bytes32]] = {a.name() for a in coin_announcements_to_consume}
@ -681,19 +682,18 @@ class CATWallet:
announcement: Announcement
for coin in cat_coins:
extra_conditions: List[Condition] = []
if cat_discrepancy is not None:
extra_conditions.append(
UnknownCondition(
opcode=Program.to(51),
args=[
Program.to(None),
Program.to(-113),
tail_reveal,
tail_solution,
],
)
cat_condition = UnknownCondition(
opcode=Program.to(51),
args=[
Program.to(None),
Program.to(-113),
tail_reveal,
tail_solution,
],
)
if first:
extra_conditions = (*extra_conditions, cat_condition)
if first:
first = False
announcement = Announcement(coin.name(), std_hash(b"".join([c.name() for c in cat_coins])))
@ -778,6 +778,7 @@ class CATWallet:
memos: Optional[List[List[bytes]]] = None,
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> List[TransactionRecord]:
# (extra_delta, tail_reveal, tail_solution)
@ -807,6 +808,7 @@ class CATWallet:
coins=coins,
coin_announcements_to_consume=coin_announcements_to_consume,
puzzle_announcements_to_consume=puzzle_announcements_to_consume,
extra_conditions=extra_conditions,
)
spend_bundle = await self.sign(unsigned_spend_bundle)
# TODO add support for array in stored records

View File

@ -20,6 +20,7 @@ from chia.types.coin_spend import CoinSpend
from chia.types.spend_bundle import SpendBundle
from chia.util.condition_tools import conditions_dict_for_solution, pkm_pairs_for_conditions_dict
from chia.util.ints import uint32, uint64, uint128
from chia.wallet.conditions import Condition
from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.derive_keys import master_sk_to_wallet_sk_unhardened
from chia.wallet.did_wallet import did_wallet_puzzles
@ -531,7 +532,9 @@ class DIDWallet:
def get_name(self):
return self.wallet_info.name
async def create_update_spend(self, tx_config: TXConfig, fee: uint64 = uint64(0)):
async def create_update_spend(
self, tx_config: TXConfig, fee: uint64 = uint64(0), extra_conditions: Tuple[Condition, ...] = tuple()
):
assert self.did_info.current_inner is not None
assert self.did_info.origin_coin is not None
coin = await self.get_coin()
@ -543,6 +546,7 @@ class DIDWallet:
p2_solution = self.standard_wallet.make_solution(
primaries=[Payment(new_inner_puzzle.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()])],
coin_announcements={coin.name()},
conditions=extra_conditions,
)
innersol: Program = Program.to([1, p2_solution])
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
@ -625,6 +629,7 @@ class DIDWallet:
fee: uint64,
with_recovery: bool,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
"""
Transfer the current DID to another owner
@ -651,6 +656,7 @@ class DIDWallet:
p2_solution = self.standard_wallet.make_solution(
primaries=[Payment(new_did_puzhash, uint64(coin.amount), [new_puzhash])],
coin_announcements={coin.name()},
conditions=extra_conditions,
)
# Need to include backup list reveal here, even we are don't recover
# innerpuz solution is
@ -720,6 +726,7 @@ class DIDWallet:
puzzle_announcements: Optional[Set[bytes]] = None,
coin_announcements_to_assert: Optional[Set[Announcement]] = None,
puzzle_announcements_to_assert: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
assert self.did_info.current_inner is not None
assert self.did_info.origin_coin is not None
@ -750,6 +757,7 @@ class DIDWallet:
puzzle_announcements_to_assert={a.name() for a in puzzle_announcements_to_assert}
if puzzle_announcements_to_assert is not None
else None,
conditions=extra_conditions,
)
# innerpuz solution is (mode p2_solution)
innersol: Program = Program.to([1, p2_solution])
@ -833,7 +841,12 @@ class DIDWallet:
# Pushes a SpendBundle to create a message coin on the blockchain
# Returns a SpendBundle for the recoverer to spend the message coin
async def create_attestment(
self, recovering_coin_name: bytes32, newpuz: bytes32, pubkey: G1Element, tx_config: TXConfig
self,
recovering_coin_name: bytes32,
newpuz: bytes32,
pubkey: G1Element,
tx_config: TXConfig,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[SpendBundle, str]:
"""
Create an attestment
@ -857,6 +870,7 @@ class DIDWallet:
Payment(innerpuz.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()]),
Payment(innermessage, uint64(0)),
],
conditions=extra_conditions,
)
innersol = Program.to([1, p2_solution])
@ -1276,14 +1290,21 @@ class DIDWallet:
await self.wallet_state_manager.add_pending_transaction(did_record)
return full_spend
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program):
async def generate_eve_spend(
self,
coin: Coin,
full_puzzle: Program,
innerpuz: Program,
extra_conditions: Tuple[Condition, ...] = tuple(),
):
assert self.did_info.origin_coin is not None
uncurried = did_wallet_puzzles.uncurry_innerpuz(innerpuz)
assert uncurried is not None
p2_puzzle = uncurried[0]
# innerpuz solution is (mode p2_solution)
p2_solution = self.standard_wallet.make_solution(
primaries=[Payment(innerpuz.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()])]
primaries=[Payment(innerpuz.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()])],
conditions=extra_conditions,
)
innersol = Program.to([1, p2_solution])
# full solution is (lineage_proof my_amount inner_solution)

View File

@ -340,6 +340,7 @@ class NFTWallet:
did_id: Optional[bytes] = None,
fee: uint64 = uint64(0),
push_tx: bool = True,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Optional[SpendBundle]:
"""
This must be called under the wallet state manager lock
@ -403,6 +404,7 @@ class NFTWallet:
False,
announcement_set,
origin_id=origin.name(),
extra_conditions=extra_conditions,
)
genesis_launcher_solution = Program.to([eve_fullpuz_hash, amount, []])
@ -494,6 +496,7 @@ class NFTWallet:
uri: str,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Optional[SpendBundle]:
uncurried_nft = UncurriedNFT.uncurry(*nft_coin_info.full_puzzle.uncurry())
assert uncurried_nft is not None
@ -511,6 +514,7 @@ class NFTWallet:
fee,
{nft_coin_info.coin},
metadata_update=(key, uri),
extra_conditions=extra_conditions,
)
for tx in txs:
await self.wallet_state_manager.add_pending_transaction(tx)
@ -622,6 +626,7 @@ class NFTWallet:
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
ignore_max_send_amount: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> List[TransactionRecord]:
nft_coin: Optional[NFTCoinInfo] = kwargs.get("nft_coin", None)
@ -655,6 +660,7 @@ class NFTWallet:
new_did_inner_hash=new_did_inner_hash,
trade_prices_list=trade_prices_list,
metadata_update=metadata_update,
extra_conditions=extra_conditions,
)
spend_bundle = await self.sign(unsigned_spend_bundle)
spend_bundle = SpendBundle.aggregate([spend_bundle] + additional_bundles)
@ -701,6 +707,7 @@ class NFTWallet:
trade_prices_list: Optional[Program] = None,
metadata_update: Optional[Tuple[str, str]] = None,
nft_coin: Optional[NFTCoinInfo] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[SpendBundle, Optional[TransactionRecord]]:
if nft_coin is None:
if coins is None or not len(coins) == 1:
@ -730,7 +737,6 @@ class NFTWallet:
announcement_to_make = None
chia_tx = None
extra_conditions: List[Condition] = []
unft = UncurriedNFT.uncurry(*nft_coin.full_puzzle.uncurry())
assert unft is not None
if unft.supports_did:
@ -743,7 +749,8 @@ class NFTWallet:
)
if derivation_record is not None:
new_owner = unft.owner_did
extra_conditions.append(
extra_conditions = (
*extra_conditions,
UnknownCondition(
opcode=Program.to(-10),
args=[
@ -751,17 +758,18 @@ class NFTWallet:
Program.to(trade_prices_list),
Program.to(new_did_inner_hash),
],
)
),
)
if metadata_update is not None:
extra_conditions.append(
extra_conditions = (
*extra_conditions,
UnknownCondition(
opcode=Program.to(-24),
args=[
NFT_METADATA_UPDATER,
Program.to(metadata_update),
],
)
),
)
innersol: Program = self.standard_wallet.make_solution(
@ -1107,6 +1115,7 @@ class NFTWallet:
tx_config: TXConfig,
fee: uint64 = uint64(0),
announcement_ids: List[bytes32] = [],
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
self.log.debug("Setting NFT DID with parameters: nft=%s did=%s", nft_list, did_id)
did_inner_hash = b""
@ -1127,6 +1136,7 @@ class NFTWallet:
puzzle_hashes_to_sign = [unft.p2_puzzle.get_tree_hash()]
if not first:
fee = uint64(0)
extra_conditions = tuple()
nft_tx_record.extend(
await self.generate_signed_transaction(
[uint64(nft_coin_info.coin.amount)],
@ -1136,6 +1146,7 @@ class NFTWallet:
{nft_coin_info.coin},
new_owner=did_id,
new_did_inner_hash=did_inner_hash,
extra_conditions=extra_conditions,
)
)
first = False
@ -1158,6 +1169,7 @@ class NFTWallet:
puzzle_hash: bytes32,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
self.log.debug("Transfer NFTs %s to %s", nft_list, puzzle_hash.hex())
nft_tx_record = []
@ -1167,6 +1179,7 @@ class NFTWallet:
for nft_coin_info in nft_list:
if not first:
fee = uint64(0)
extra_conditions = tuple()
nft_tx_record.extend(
await self.generate_signed_transaction(
[uint64(nft_coin_info.coin.amount)],
@ -1176,6 +1189,7 @@ class NFTWallet:
fee=fee,
new_owner=b"",
new_did_inner_hash=b"",
extra_conditions=extra_conditions,
)
)
first = False
@ -1197,6 +1211,7 @@ class NFTWallet:
did_id: bytes,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> SpendBundle:
self.log.debug("Setting NFT DID with parameters: nft=%s did=%s", nft_coin_info, did_id)
unft = UncurriedNFT.uncurry(*nft_coin_info.full_puzzle.uncurry())
@ -1218,6 +1233,7 @@ class NFTWallet:
new_owner=did_id,
new_did_inner_hash=did_inner_hash,
additional_bundles=additional_bundles,
extra_conditions=extra_conditions,
)
spend_bundle = SpendBundle.aggregate([x.spend_bundle for x in nft_tx_record if x.spend_bundle is not None])
if spend_bundle:
@ -1243,6 +1259,7 @@ class NFTWallet:
did_coin: Optional[Coin] = None,
did_lineage_parent: Optional[bytes32] = None,
fee: Optional[uint64] = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> SpendBundle:
"""
Minting NFTs from the DID linked wallet, also used for bulk minting NFTs.
@ -1475,6 +1492,7 @@ class NFTWallet:
coin_announcements=did_coin_announcement,
coin_announcements_to_assert=did_announcements,
puzzle_announcements_to_assert=puzzle_assertions,
conditions=extra_conditions,
)
did_inner_sol: Program = Program.to([1, did_p2_solution])
did_full_puzzle: Program = chia.wallet.singleton.create_singleton_puzzle(
@ -1524,6 +1542,7 @@ class NFTWallet:
xch_coins: Optional[Set[Coin]] = None,
xch_change_ph: Optional[bytes32] = None,
fee: Optional[uint64] = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> SpendBundle:
"""
Minting NFTs from a single XCH spend using intermediate launcher puzzle
@ -1695,6 +1714,7 @@ class NFTWallet:
coin_announcements=xch_announcement if len(xch_coins) > 1 else None,
coin_announcements_to_assert=coin_announcements,
puzzle_announcements_to_assert=puzzle_assertions,
conditions=extra_conditions,
)
primary_announcement_hash = Announcement(xch_coin.name(), message).name()
first = False

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import dataclasses
import logging
from typing import Any, Dict, List, Optional, Set
from typing import Any, Dict, List, Optional, Set, Tuple
from blspy import G2Element
@ -15,6 +15,7 @@ from chia.types.coin_spend import CoinSpend
from chia.types.spend_bundle import SpendBundle
from chia.util.db_wrapper import DBWrapper2
from chia.util.ints import uint32, uint64
from chia.wallet.conditions import Condition
from chia.wallet.notification_store import Notification, NotificationStore
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.compute_memos import compute_memos_for_spend
@ -83,7 +84,13 @@ class NotificationManager:
return True
async def send_new_notification(
self, target: bytes32, msg: bytes, amount: uint64, tx_config: TXConfig, fee: uint64 = uint64(0)
self,
target: bytes32,
msg: bytes,
amount: uint64,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> TransactionRecord:
coins: Set[Coin] = await self.wallet_state_manager.main_wallet.select_coins(
uint64(amount + fee), tx_config.coin_selection_config
@ -107,6 +114,7 @@ class NotificationManager:
origin_id=origin_coin,
coin_announcements_to_consume={Announcement(notification_coin.name(), b"")},
memos=[target, msg],
extra_conditions=extra_conditions,
)
full_tx: TransactionRecord = dataclasses.replace(
chia_tx, spend_bundle=SpendBundle.aggregate([chia_tx.spend_bundle, extra_spend_bundle])

View File

@ -17,6 +17,7 @@ from chia.types.spend_bundle import SpendBundle
from chia.util.db_wrapper import DBWrapper2
from chia.util.hash import std_hash
from chia.util.ints import uint32, uint64
from chia.wallet.conditions import Condition
from chia.wallet.db_wallet.db_wallet_puzzles import ACS_MU_PH
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
from chia.wallet.outer_puzzles import AssetType
@ -228,6 +229,7 @@ class TradeManager:
fee: uint64 = uint64(0),
secure: bool = True, # Cancel with a transaction on chain
trade_cache: Dict[bytes32, TradeRecord] = {}, # Optional pre-fetched trade records for optimization
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Optional[List[TransactionRecord]]:
"""This will create a transaction that includes coins that were offered"""
@ -282,6 +284,7 @@ class TradeManager:
fee=fee_to_pay,
coins=selected_coins,
ignore_max_send_amount=True,
extra_conditions=extra_conditions,
)
if tx is not None and tx.spend_bundle is not None:
bundles.append(tx.spend_bundle)
@ -298,6 +301,7 @@ class TradeManager:
fee=fee_to_pay,
coins={coin},
ignore_max_send_amount=True,
extra_conditions=extra_conditions,
)
for tx in txs:
if tx is not None and tx.spend_bundle is not None:
@ -305,6 +309,7 @@ class TradeManager:
cancellation_additions.extend(tx.spend_bundle.additions())
all_txs.append(dataclasses.replace(tx, spend_bundle=None))
fee_to_pay = uint64(0)
extra_conditions = tuple()
all_txs.append(
TransactionRecord(
@ -358,6 +363,7 @@ class TradeManager:
solver: Optional[Solver] = None,
fee: uint64 = uint64(0),
validate_only: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
taking: bool = False,
) -> Union[Tuple[Literal[True], TradeRecord, None], Tuple[Literal[False], None, str]]:
if driver_dict is None:
@ -370,6 +376,7 @@ class TradeManager:
driver_dict,
solver,
fee=fee,
extra_conditions=extra_conditions,
taking=taking,
)
if not result[0] or result[1] is None:
@ -404,6 +411,7 @@ class TradeManager:
driver_dict: Optional[Dict[bytes32, PuzzleInfo]] = None,
solver: Optional[Solver] = None,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
taking: bool = False,
) -> Union[Tuple[Literal[True], Offer, None], Tuple[Literal[False], None, str]]:
"""
@ -526,6 +534,7 @@ class TradeManager:
fee=fee_left_to_pay,
coins=set(selected_coins),
puzzle_announcements_to_consume=announcements_to_assert,
extra_conditions=extra_conditions,
)
all_transactions.append(tx)
elif wallet.type() == WalletType.NFT:
@ -540,6 +549,7 @@ class TradeManager:
fee=fee_left_to_pay,
coins=set(selected_coins),
puzzle_announcements_to_consume=announcements_to_assert,
extra_conditions=extra_conditions,
)
all_transactions.extend(txs)
else:
@ -551,11 +561,13 @@ class TradeManager:
fee=fee_left_to_pay,
coins=set(selected_coins),
puzzle_announcements_to_consume=announcements_to_assert,
extra_conditions=extra_conditions,
add_authorizations_to_cr_cats=False,
)
all_transactions.extend(txs)
fee_left_to_pay = uint64(0)
extra_conditions = tuple()
total_spend_bundle = SpendBundle.aggregate(
[x.spend_bundle for x in all_transactions if x.spend_bundle is not None]
@ -696,6 +708,7 @@ class TradeManager:
tx_config: TXConfig,
solver: Optional[Solver] = None,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[TradeRecord, List[TransactionRecord]]:
if solver is None:
solver = Solver({})
@ -727,6 +740,7 @@ class TradeManager:
offer.driver_dict,
solver,
fee=fee,
extra_conditions=extra_conditions,
taking=True,
)
if not result[0] or result[1] is None:

View File

@ -24,6 +24,7 @@ from chia.wallet.cat_wallet.cat_info import CRCATInfo
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
from chia.wallet.cat_wallet.cat_wallet import CATWallet
from chia.wallet.coin_selection import select_coins
from chia.wallet.conditions import Condition, UnknownCondition
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.payment import Payment
@ -388,6 +389,7 @@ class CRCATWallet(CATWallet):
coins: Optional[Set[Coin]] = None,
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
add_authorizations_to_cr_cats: bool = True,
) -> Tuple[SpendBundle, List[TransactionRecord]]:
if coin_announcements_to_consume is not None: # pragma: no cover
@ -485,6 +487,19 @@ class CRCATWallet(CATWallet):
self.info.authorized_providers, self.info.proofs_checker.flags
)
if cat_discrepancy is not None:
cat_condition = UnknownCondition(
opcode=Program.to(51),
args=[
Program.to(None),
Program.to(-113),
tail_reveal,
tail_solution,
],
)
if first:
extra_conditions = (*extra_conditions, cat_condition)
crcat: CRCAT = self.coin_record_to_crcat(coin)
vc_announcements_to_make.append(crcat.expected_announcement())
if first:
@ -502,6 +517,7 @@ class CRCATWallet(CATWallet):
coin_announcements={announcement.message},
coin_announcements_to_assert=coin_announcements_bytes,
puzzle_announcements_to_assert=puzzle_announcements_bytes,
conditions=extra_conditions,
)
elif regular_chia_to_claim > fee:
chia_tx, xch_announcement = await self.create_tandem_xch_tx(
@ -514,6 +530,7 @@ class CRCATWallet(CATWallet):
primaries=primaries,
coin_announcements={announcement.message},
coin_announcements_to_assert={xch_announcement.name()},
conditions=extra_conditions,
)
else:
innersol = self.standard_wallet.make_solution(
@ -521,17 +538,13 @@ class CRCATWallet(CATWallet):
coin_announcements={announcement.message},
coin_announcements_to_assert=coin_announcements_bytes,
puzzle_announcements_to_assert=puzzle_announcements_bytes,
conditions=extra_conditions,
)
else:
innersol = self.standard_wallet.make_solution(
primaries=[],
coin_announcements_to_assert={announcement.name()},
)
if first and cat_discrepancy is not None:
# TODO: This line is a hack, make_solution should allow us to pass extra conditions to it
innersol = Program.to(
[[], (1, Program.to([51, None, -113, tail_reveal, tail_solution]).cons(innersol.at("rfr"))), []]
)
inner_derivation_record = (
await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
crcat.inner_puzzle_hash
@ -611,6 +624,7 @@ class CRCATWallet(CATWallet):
memos: Optional[List[List[bytes]]] = None,
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> List[TransactionRecord]:
# (extra_delta, tail_reveal, tail_solution)
@ -651,6 +665,7 @@ class CRCATWallet(CATWallet):
coins=coins,
coin_announcements_to_consume=coin_announcements_to_consume,
puzzle_announcements_to_consume=puzzle_announcements_to_consume,
extra_conditions=extra_conditions,
add_authorizations_to_cr_cats=add_authorizations_to_cr_cats,
)

View File

@ -10,6 +10,7 @@ from chia.types.coin_spend import CoinSpend, compute_additions
from chia.util.hash import std_hash
from chia.util.ints import uint64
from chia.util.streamable import Streamable, streamable
from chia.wallet.conditions import Condition
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
from chia.wallet.puzzles.singleton_top_layer_v1_1 import (
@ -337,6 +338,7 @@ class VerifiedCredential(Streamable):
new_inner_puzzle_hash: bytes32,
memos: List[bytes32],
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[Program, List[CoinSpend], _T_VerifiedCredential]:
"""
Launch a VC.
@ -408,6 +410,7 @@ class VerifiedCredential(Streamable):
[52, fee],
[61, std_hash(launcher_coin.name() + launcher_solution.get_tree_hash())],
[61, std_hash(second_launcher_coin.name() + launch_dpuz.get_tree_hash())],
*[cond.to_program() for cond in extra_conditions],
]
)

View File

@ -20,7 +20,7 @@ from chia.types.coin_spend import CoinSpend
from chia.types.spend_bundle import SpendBundle
from chia.util.hash import std_hash
from chia.util.ints import uint32, uint64, uint128
from chia.wallet.conditions import UnknownCondition
from chia.wallet.conditions import Condition, UnknownCondition
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.payment import Payment
from chia.wallet.puzzle_drivers import Solver
@ -155,6 +155,7 @@ class VCWallet:
tx_config: TXConfig,
inner_puzzle_hash: Optional[bytes32] = None,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> Tuple[VCRecord, List[TransactionRecord]]:
"""
Given the DID ID of a proof provider, mint a brand new VC with an empty slot for proofs.
@ -184,6 +185,7 @@ class VCWallet:
inner_puzzle_hash,
[inner_puzzle_hash],
fee=fee,
extra_conditions=extra_conditions,
)
solution = solution_for_conditions(dpuz.rest())
original_puzzle = await self.standard_wallet.puzzle_for_puzzle_hash(original_coin.puzzle_hash)
@ -224,6 +226,7 @@ class VCWallet:
puzzle_announcements: Optional[Set[bytes]] = None,
coin_announcements_to_consume: Optional[Set[Announcement]] = None,
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> List[TransactionRecord]:
new_proof_hash: Optional[bytes32] = kwargs.get(
@ -296,13 +299,14 @@ class VCWallet:
magic_condition = vc_record.vc.magic_condition_for_self_revoke()
else:
magic_condition = vc_record.vc.standard_magic_condition()
extra_conditions = (*extra_conditions, UnknownCondition.from_program(magic_condition))
innersol: Program = self.standard_wallet.make_solution(
primaries=primaries,
coin_announcements=coin_announcements,
puzzle_announcements=puzzle_announcements,
coin_announcements_to_assert=coin_announcements_bytes,
puzzle_announcements_to_assert=puzzle_announcements_bytes,
conditions=[UnknownCondition.from_program(magic_condition)],
conditions=extra_conditions,
)
did_announcement, coin_spend, vc = vc_record.vc.do_spend(inner_puzzle, innersol, new_proof_hash)
spend_bundles = [await self.wallet_state_manager.sign_transaction([coin_spend])]
@ -358,6 +362,7 @@ class VCWallet:
peer: WSChiaConnection,
tx_config: TXConfig,
fee: uint64 = uint64(0),
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[TransactionRecord]:
vc_coin_states: List[CoinState] = await self.wallet_state_manager.wallet_node.get_coin_state(
[parent_id], peer=peer
@ -405,6 +410,7 @@ class VCWallet:
tx_config,
puzzle_announcements={expected_did_announcement},
coin_announcements_to_assert={vc_announcement},
extra_conditions=extra_conditions,
)
final_bundle: SpendBundle = SpendBundle.aggregate([SpendBundle([vc_spend], G2Element()), did_spend])
tx: TransactionRecord = TransactionRecord(

View File

@ -203,7 +203,7 @@ class Wallet:
coin_announcements_to_assert: Optional[Set[bytes32]] = None,
puzzle_announcements: Optional[Set[bytes]] = None,
puzzle_announcements_to_assert: Optional[Set[bytes32]] = None,
conditions: List[Condition] = [],
conditions: Tuple[Condition, ...] = tuple(),
fee: uint64 = uint64(0),
) -> Program:
assert fee >= 0
@ -277,6 +277,7 @@ class Wallet:
memos: Optional[List[bytes]] = None,
negative_change_allowed: bool = False,
puzzle_decorator_override: Optional[List[Dict[str, Any]]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[CoinSpend]:
"""
Generates a unsigned transaction in form of List(Puzzle, Solutions)
@ -371,6 +372,7 @@ class Wallet:
coin_announcements={message},
coin_announcements_to_assert=coin_announcements_bytes,
puzzle_announcements_to_assert=puzzle_announcements_bytes,
conditions=extra_conditions,
)
solution = decorator_manager.solve(inner_puzzle, target_primary, solution)
primary_announcement_hash = Announcement(coin.name(), message).name()
@ -427,6 +429,7 @@ class Wallet:
puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
memos: Optional[List[bytes]] = None,
puzzle_decorator_override: Optional[List[Dict[str, Any]]] = None,
extra_conditions: Tuple[Condition, ...] = tuple(),
**kwargs: Unpack[GSTOptionalArgs],
) -> TransactionRecord:
origin_id: Optional[bytes32] = kwargs.get("origin_id", None)
@ -456,6 +459,7 @@ class Wallet:
memos,
negative_change_allowed,
puzzle_decorator_override=puzzle_decorator_override,
extra_conditions=extra_conditions,
)
assert len(transaction) > 0
self.log.info("About to sign a transaction: %s", transaction)

View File

@ -18,6 +18,7 @@ from typing import (
List,
Optional,
Set,
Tuple,
Type,
TypeVar,
Union,
@ -63,6 +64,7 @@ from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
from chia.wallet.cat_wallet.cat_info import CATInfo, CRCATInfo
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, CAT_MOD_HASH, construct_cat_puzzle, match_cat_puzzle
from chia.wallet.cat_wallet.cat_wallet import CATWallet
from chia.wallet.conditions import Condition
from chia.wallet.db_wallet.db_wallet_puzzles import MIRROR_PUZZLE_HASH
from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.derive_keys import (
@ -809,7 +811,12 @@ class WalletStateManager:
await self.spend_clawback_coins(clawback_coins, tx_fee, tx_config)
async def spend_clawback_coins(
self, clawback_coins: Dict[Coin, ClawbackMetadata], fee: uint64, tx_config: TXConfig, force: bool = False
self,
clawback_coins: Dict[Coin, ClawbackMetadata],
fee: uint64,
tx_config: TXConfig,
force: bool = False,
extra_conditions: Tuple[Condition, ...] = tuple(),
) -> List[bytes32]:
assert len(clawback_coins) > 0
coin_spends: List[CoinSpend] = []
@ -850,6 +857,7 @@ class WalletStateManager:
)
],
coin_announcements=None if len(coin_spends) > 0 or fee == 0 else {message},
conditions=extra_conditions,
)
coin_spend: CoinSpend = generate_clawback_spend_bundle(coin, metadata, inner_puzzle, inner_solution)
coin_spends.append(coin_spend)

View File

@ -0,0 +1,55 @@
from __future__ import annotations
from typing import Optional
import click
from click.testing import CliRunner
from chia.cmds.cmds_util import timelock_args
def test_timelock_args() -> None:
@click.command()
@timelock_args
def test_cmd(
valid_at: Optional[int],
expires_at: Optional[int],
) -> None:
print(valid_at)
print(expires_at)
runner = CliRunner()
result = runner.invoke(
test_cmd,
[
"--valid-at",
"0",
"--expires-at",
"0",
],
catch_exceptions=False,
)
assert "0\n0\n" == result.output
result = runner.invoke(
test_cmd,
[
"--valid-at",
"4294967295",
"--expires-at",
"4294967295",
],
catch_exceptions=False,
)
assert "4294967295\n4294967295\n" == result.output
result = runner.invoke(
test_cmd,
[],
catch_exceptions=False,
)
assert "None\nNone\n" == result.output