mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2025-01-07 17:59:35 +03:00
Add valid_times to Offer object (#16255)
This commit is contained in:
parent
ab07de72d5
commit
b6dae184a1
@ -613,7 +613,7 @@ async def print_trade_record(record: TradeRecord, wallet_client: WalletRpcClient
|
|||||||
if summaries:
|
if summaries:
|
||||||
print("Summary:")
|
print("Summary:")
|
||||||
offer = Offer.from_bytes(record.offer)
|
offer = Offer.from_bytes(record.offer)
|
||||||
offered, requested, _ = offer.summary()
|
offered, requested, _, _ = offer.summary()
|
||||||
outbound_balances: Dict[str, int] = offer.get_pending_amounts()
|
outbound_balances: Dict[str, int] = offer.get_pending_amounts()
|
||||||
fees: Decimal = Decimal(offer.fees())
|
fees: Decimal = Decimal(offer.fees())
|
||||||
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
||||||
@ -701,7 +701,7 @@ async def take_offer(
|
|||||||
print("Please enter a valid offer file or hex blob")
|
print("Please enter a valid offer file or hex blob")
|
||||||
return
|
return
|
||||||
|
|
||||||
offered, requested, _ = offer.summary()
|
offered, requested, _, _ = offer.summary()
|
||||||
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
||||||
network_xch = AddressType.XCH.hrp(config).upper()
|
network_xch = AddressType.XCH.hrp(config).upper()
|
||||||
print("Summary:")
|
print("Summary:")
|
||||||
|
@ -8,7 +8,7 @@ import aiohttp
|
|||||||
|
|
||||||
from chia.types.blockchain_format.coin import Coin
|
from chia.types.blockchain_format.coin import Coin
|
||||||
from chia.util.json_util import obj_to_response
|
from chia.util.json_util import obj_to_response
|
||||||
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_from_json_dicts
|
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_from_json_dicts, parse_timelock_info
|
||||||
from chia.wallet.util.tx_config import TXConfig, TXConfigLoader
|
from chia.wallet.util.tx_config import TXConfig, TXConfigLoader
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -69,6 +69,16 @@ def tx_endpoint(
|
|||||||
if "extra_conditions" in request:
|
if "extra_conditions" in request:
|
||||||
extra_conditions = tuple(conditions_from_json_dicts(request["extra_conditions"]))
|
extra_conditions = tuple(conditions_from_json_dicts(request["extra_conditions"]))
|
||||||
extra_conditions = (*extra_conditions, *ConditionValidTimes.from_json_dict(request).to_conditions())
|
extra_conditions = (*extra_conditions, *ConditionValidTimes.from_json_dict(request).to_conditions())
|
||||||
|
|
||||||
|
valid_times: ConditionValidTimes = parse_timelock_info(extra_conditions)
|
||||||
|
if (
|
||||||
|
valid_times.max_secs_after_created is not None
|
||||||
|
or valid_times.min_secs_since_created is not None
|
||||||
|
or valid_times.max_blocks_after_created is not None
|
||||||
|
or valid_times.min_blocks_since_created is not None
|
||||||
|
):
|
||||||
|
raise ValueError("Relative timelocks are not currently supported in the RPC")
|
||||||
|
|
||||||
return await func(self, request, *args, tx_config=tx_config, extra_conditions=extra_conditions, **kwargs)
|
return await func(self, request, *args, tx_config=tx_config, extra_conditions=extra_conditions, **kwargs)
|
||||||
|
|
||||||
return rpc_endpoint
|
return rpc_endpoint
|
||||||
|
@ -1730,11 +1730,27 @@ class WalletRpcApi:
|
|||||||
###
|
###
|
||||||
|
|
||||||
offer = Offer.from_bech32(offer_hex)
|
offer = Offer.from_bech32(offer_hex)
|
||||||
offered, requested, infos = offer.summary()
|
offered, requested, infos, valid_times = offer.summary()
|
||||||
|
|
||||||
if request.get("advanced", False):
|
if request.get("advanced", False):
|
||||||
response = {
|
response = {
|
||||||
"summary": {"offered": offered, "requested": requested, "fees": offer.fees(), "infos": infos},
|
"summary": {
|
||||||
|
"offered": offered,
|
||||||
|
"requested": requested,
|
||||||
|
"fees": offer.fees(),
|
||||||
|
"infos": infos,
|
||||||
|
"valid_times": {
|
||||||
|
k: v
|
||||||
|
for k, v in valid_times.to_json_dict().items()
|
||||||
|
if k
|
||||||
|
not in (
|
||||||
|
"max_secs_after_created",
|
||||||
|
"min_secs_since_created",
|
||||||
|
"max_blocks_after_created",
|
||||||
|
"min_blocks_since_created",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
"id": offer.name(),
|
"id": offer.name(),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
@ -880,8 +880,24 @@ class TradeManager:
|
|||||||
):
|
):
|
||||||
return await DataLayerWallet.get_offer_summary(offer)
|
return await DataLayerWallet.get_offer_summary(offer)
|
||||||
# Otherwise just return the same thing as the RPC normally does
|
# Otherwise just return the same thing as the RPC normally does
|
||||||
offered, requested, infos = offer.summary()
|
offered, requested, infos, valid_times = offer.summary()
|
||||||
return {"offered": offered, "requested": requested, "fees": offer.fees(), "infos": infos}
|
return {
|
||||||
|
"offered": offered,
|
||||||
|
"requested": requested,
|
||||||
|
"fees": offer.fees(),
|
||||||
|
"infos": infos,
|
||||||
|
"valid_times": {
|
||||||
|
k: v
|
||||||
|
for k, v in valid_times.to_json_dict().items()
|
||||||
|
if k
|
||||||
|
not in (
|
||||||
|
"max_secs_after_created",
|
||||||
|
"min_secs_since_created",
|
||||||
|
"max_blocks_after_created",
|
||||||
|
"min_blocks_since_created",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
async def check_for_final_modifications(
|
async def check_for_final_modifications(
|
||||||
self, offer: Offer, solver: Solver, tx_config: TXConfig
|
self, offer: Offer, solver: Solver, tx_config: TXConfig
|
||||||
|
@ -38,7 +38,7 @@ class TradeRecordOld(Streamable):
|
|||||||
formatted["status"] = TradeStatus(self.status).name
|
formatted["status"] = TradeStatus(self.status).name
|
||||||
offer_to_summarize: bytes = self.offer if self.taken_offer is None else self.taken_offer
|
offer_to_summarize: bytes = self.offer if self.taken_offer is None else self.taken_offer
|
||||||
offer = Offer.from_bytes(offer_to_summarize)
|
offer = Offer.from_bytes(offer_to_summarize)
|
||||||
offered, requested, infos = offer.summary()
|
offered, requested, infos, _ = offer.summary()
|
||||||
formatted["summary"] = {
|
formatted["summary"] = {
|
||||||
"offered": offered,
|
"offered": offered,
|
||||||
"requested": requested,
|
"requested": requested,
|
||||||
|
@ -16,6 +16,7 @@ from chia.types.spend_bundle import SpendBundle
|
|||||||
from chia.util.bech32m import bech32_decode, bech32_encode, convertbits
|
from chia.util.bech32m import bech32_decode, bech32_encode, convertbits
|
||||||
from chia.util.errors import Err, ValidationError
|
from chia.util.errors import Err, ValidationError
|
||||||
from chia.util.ints import uint64
|
from chia.util.ints import uint64
|
||||||
|
from chia.wallet.conditions import Condition, ConditionValidTimes, parse_conditions_non_consensus, parse_timelock_info
|
||||||
from chia.wallet.outer_puzzles import (
|
from chia.wallet.outer_puzzles import (
|
||||||
construct_puzzle,
|
construct_puzzle,
|
||||||
create_asset_id,
|
create_asset_id,
|
||||||
@ -77,6 +78,7 @@ class Offer:
|
|||||||
_additions: Dict[Coin, List[Coin]] = field(init=False)
|
_additions: Dict[Coin, List[Coin]] = field(init=False)
|
||||||
_offered_coins: Dict[Optional[bytes32], List[Coin]] = field(init=False)
|
_offered_coins: Dict[Optional[bytes32], List[Coin]] = field(init=False)
|
||||||
_final_spend_bundle: Optional[SpendBundle] = field(init=False)
|
_final_spend_bundle: Optional[SpendBundle] = field(init=False)
|
||||||
|
_conditions: Optional[Dict[Coin, List[Condition]]] = field(init=False)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def ph() -> bytes32:
|
def ph() -> bytes32:
|
||||||
@ -148,6 +150,40 @@ class Offer:
|
|||||||
if max_cost < 0:
|
if max_cost < 0:
|
||||||
raise ValidationError(Err.BLOCK_COST_EXCEEDS_MAX, "compute_additions for CoinSpend")
|
raise ValidationError(Err.BLOCK_COST_EXCEEDS_MAX, "compute_additions for CoinSpend")
|
||||||
object.__setattr__(self, "_additions", adds)
|
object.__setattr__(self, "_additions", adds)
|
||||||
|
object.__setattr__(self, "_conditions", None)
|
||||||
|
|
||||||
|
def conditions(self) -> Dict[Coin, List[Condition]]:
|
||||||
|
if self._conditions is None:
|
||||||
|
conditions: Dict[Coin, List[Condition]] = {}
|
||||||
|
max_cost = DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
|
||||||
|
for cs in self._bundle.coin_spends:
|
||||||
|
try:
|
||||||
|
cost, conds = cs.puzzle_reveal.run_with_cost(max_cost, cs.solution)
|
||||||
|
max_cost -= cost
|
||||||
|
conditions[cs.coin] = parse_conditions_non_consensus(conds.as_iter())
|
||||||
|
except Exception: # pragma: no cover
|
||||||
|
continue
|
||||||
|
if max_cost < 0: # pragma: no cover
|
||||||
|
raise ValidationError(Err.BLOCK_COST_EXCEEDS_MAX, "computing conditions for CoinSpend")
|
||||||
|
object.__setattr__(self, "_conditions", conditions)
|
||||||
|
assert self._conditions is not None, "self._conditions is None"
|
||||||
|
return self._conditions
|
||||||
|
|
||||||
|
def valid_times(self) -> Dict[Coin, ConditionValidTimes]:
|
||||||
|
return {coin: parse_timelock_info(conditions) for coin, conditions in self.conditions().items()}
|
||||||
|
|
||||||
|
def absolute_valid_times_ban_relatives(self) -> ConditionValidTimes:
|
||||||
|
valid_times: ConditionValidTimes = parse_timelock_info(
|
||||||
|
[c for conditions in self.conditions().values() for c in conditions]
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
valid_times.max_secs_after_created is not None
|
||||||
|
or valid_times.min_secs_since_created is not None
|
||||||
|
or valid_times.max_blocks_after_created is not None
|
||||||
|
or valid_times.min_blocks_since_created is not None
|
||||||
|
):
|
||||||
|
raise ValueError("Offers with relative timelocks are not currently supported")
|
||||||
|
return valid_times
|
||||||
|
|
||||||
def additions(self) -> List[Coin]:
|
def additions(self) -> List[Coin]:
|
||||||
return [c for additions in self._additions.values() for c in additions]
|
return [c for additions in self._additions.values() for c in additions]
|
||||||
@ -270,7 +306,7 @@ class Offer:
|
|||||||
return arbitrage_dict
|
return arbitrage_dict
|
||||||
|
|
||||||
# This is a method mostly for the UI that creates a JSON summary of the offer
|
# This is a method mostly for the UI that creates a JSON summary of the offer
|
||||||
def summary(self) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, Any]]]:
|
def summary(self) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, Any]], ConditionValidTimes]:
|
||||||
offered_amounts: Dict[Optional[bytes32], int] = self.get_offered_amounts()
|
offered_amounts: Dict[Optional[bytes32], int] = self.get_offered_amounts()
|
||||||
requested_amounts: Dict[Optional[bytes32], int] = self.get_requested_amounts()
|
requested_amounts: Dict[Optional[bytes32], int] = self.get_requested_amounts()
|
||||||
|
|
||||||
@ -287,7 +323,12 @@ class Offer:
|
|||||||
for key, value in self.driver_dict.items():
|
for key, value in self.driver_dict.items():
|
||||||
driver_dict[key.hex()] = value.info
|
driver_dict[key.hex()] = value.info
|
||||||
|
|
||||||
return keys_to_strings(offered_amounts), keys_to_strings(requested_amounts), driver_dict
|
return (
|
||||||
|
keys_to_strings(offered_amounts),
|
||||||
|
keys_to_strings(requested_amounts),
|
||||||
|
driver_dict,
|
||||||
|
self.absolute_valid_times_ban_relatives(),
|
||||||
|
)
|
||||||
|
|
||||||
# Also mostly for the UI, returns a dictionary of assets and how much of them is pended for this offer
|
# Also mostly for the UI, returns a dictionary of assets and how much of them is pended for this offer
|
||||||
# This method is also imperfect for sufficiently complex spends
|
# This method is also imperfect for sufficiently complex spends
|
||||||
|
@ -21,6 +21,7 @@ from chia.wallet.cat_wallet.cat_utils import (
|
|||||||
construct_cat_puzzle,
|
construct_cat_puzzle,
|
||||||
unsigned_spend_bundle_for_spendable_cats,
|
unsigned_spend_bundle_for_spendable_cats,
|
||||||
)
|
)
|
||||||
|
from chia.wallet.conditions import ConditionValidTimes
|
||||||
from chia.wallet.outer_puzzles import AssetType
|
from chia.wallet.outer_puzzles import AssetType
|
||||||
from chia.wallet.payment import Payment
|
from chia.wallet.payment import Payment
|
||||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||||
@ -288,6 +289,7 @@ class TestOfferLifecycle:
|
|||||||
},
|
},
|
||||||
{"xch": 900, str_to_tail_hash("red").hex(): 350},
|
{"xch": 900, str_to_tail_hash("red").hex(): 350},
|
||||||
driver_dict_as_infos,
|
driver_dict_as_infos,
|
||||||
|
ConditionValidTimes(),
|
||||||
)
|
)
|
||||||
assert new_offer.get_pending_amounts() == {
|
assert new_offer.get_pending_amounts() == {
|
||||||
"xch": 1200,
|
"xch": 1200,
|
||||||
|
@ -42,6 +42,7 @@ from chia.util.streamable import ConversionError, InvalidTypeError
|
|||||||
from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
|
from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
|
||||||
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
|
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.cat_wallet.cat_wallet import CATWallet
|
||||||
|
from chia.wallet.conditions import ConditionValidTimes
|
||||||
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
|
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
|
||||||
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
||||||
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
||||||
@ -1148,7 +1149,18 @@ async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment)
|
|||||||
assert id == offer.name()
|
assert id == offer.name()
|
||||||
id, advanced_summary = await wallet_1_rpc.get_offer_summary(offer, advanced=True)
|
id, advanced_summary = await wallet_1_rpc.get_offer_summary(offer, advanced=True)
|
||||||
assert id == offer.name()
|
assert id == offer.name()
|
||||||
assert summary == {"offered": {"xch": 5}, "requested": {cat_asset_id.hex(): 1}, "infos": driver_dict, "fees": 1}
|
assert summary == {
|
||||||
|
"offered": {"xch": 5},
|
||||||
|
"requested": {cat_asset_id.hex(): 1},
|
||||||
|
"infos": driver_dict,
|
||||||
|
"fees": 1,
|
||||||
|
"valid_times": {
|
||||||
|
"max_height": None,
|
||||||
|
"max_time": None,
|
||||||
|
"min_height": None,
|
||||||
|
"min_time": None,
|
||||||
|
},
|
||||||
|
}
|
||||||
assert advanced_summary == summary
|
assert advanced_summary == summary
|
||||||
|
|
||||||
id, valid = await wallet_1_rpc.check_offer_validity(offer)
|
id, valid = await wallet_1_rpc.check_offer_validity(offer)
|
||||||
@ -1298,6 +1310,14 @@ async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment)
|
|||||||
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
|
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
|
||||||
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="not currently supported"):
|
||||||
|
await wallet_1_rpc.create_offer_for_ids(
|
||||||
|
{uint32(1): -5, cat_asset_id.hex(): 1},
|
||||||
|
DEFAULT_TX_CONFIG,
|
||||||
|
driver_dict=driver_dict,
|
||||||
|
timelock_info=ConditionValidTimes(min_secs_since_created=uint64(1)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
Loading…
Reference in New Issue
Block a user