mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2025-01-06 04:07:16 +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:
|
||||
print("Summary:")
|
||||
offer = Offer.from_bytes(record.offer)
|
||||
offered, requested, _ = offer.summary()
|
||||
offered, requested, _, _ = offer.summary()
|
||||
outbound_balances: Dict[str, int] = offer.get_pending_amounts()
|
||||
fees: Decimal = Decimal(offer.fees())
|
||||
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")
|
||||
return
|
||||
|
||||
offered, requested, _ = offer.summary()
|
||||
offered, requested, _, _ = offer.summary()
|
||||
cat_name_resolver = wallet_client.cat_asset_id_to_name
|
||||
network_xch = AddressType.XCH.hrp(config).upper()
|
||||
print("Summary:")
|
||||
|
@ -8,7 +8,7 @@ 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, 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
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -69,6 +69,16 @@ def tx_endpoint(
|
||||
if "extra_conditions" in request:
|
||||
extra_conditions = tuple(conditions_from_json_dicts(request["extra_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 rpc_endpoint
|
||||
|
@ -1730,11 +1730,27 @@ class WalletRpcApi:
|
||||
###
|
||||
|
||||
offer = Offer.from_bech32(offer_hex)
|
||||
offered, requested, infos = offer.summary()
|
||||
offered, requested, infos, valid_times = offer.summary()
|
||||
|
||||
if request.get("advanced", False):
|
||||
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(),
|
||||
}
|
||||
else:
|
||||
|
@ -880,8 +880,24 @@ class TradeManager:
|
||||
):
|
||||
return await DataLayerWallet.get_offer_summary(offer)
|
||||
# Otherwise just return the same thing as the RPC normally does
|
||||
offered, requested, infos = offer.summary()
|
||||
return {"offered": offered, "requested": requested, "fees": offer.fees(), "infos": infos}
|
||||
offered, requested, infos, valid_times = offer.summary()
|
||||
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(
|
||||
self, offer: Offer, solver: Solver, tx_config: TXConfig
|
||||
|
@ -38,7 +38,7 @@ class TradeRecordOld(Streamable):
|
||||
formatted["status"] = TradeStatus(self.status).name
|
||||
offer_to_summarize: bytes = self.offer if self.taken_offer is None else self.taken_offer
|
||||
offer = Offer.from_bytes(offer_to_summarize)
|
||||
offered, requested, infos = offer.summary()
|
||||
offered, requested, infos, _ = offer.summary()
|
||||
formatted["summary"] = {
|
||||
"offered": offered,
|
||||
"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.errors import Err, ValidationError
|
||||
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 (
|
||||
construct_puzzle,
|
||||
create_asset_id,
|
||||
@ -77,6 +78,7 @@ class Offer:
|
||||
_additions: Dict[Coin, List[Coin]] = field(init=False)
|
||||
_offered_coins: Dict[Optional[bytes32], List[Coin]] = field(init=False)
|
||||
_final_spend_bundle: Optional[SpendBundle] = field(init=False)
|
||||
_conditions: Optional[Dict[Coin, List[Condition]]] = field(init=False)
|
||||
|
||||
@staticmethod
|
||||
def ph() -> bytes32:
|
||||
@ -148,6 +150,40 @@ class Offer:
|
||||
if max_cost < 0:
|
||||
raise ValidationError(Err.BLOCK_COST_EXCEEDS_MAX, "compute_additions for CoinSpend")
|
||||
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]:
|
||||
return [c for additions in self._additions.values() for c in additions]
|
||||
@ -270,7 +306,7 @@ class Offer:
|
||||
return arbitrage_dict
|
||||
|
||||
# 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()
|
||||
requested_amounts: Dict[Optional[bytes32], int] = self.get_requested_amounts()
|
||||
|
||||
@ -287,7 +323,12 @@ class Offer:
|
||||
for key, value in self.driver_dict.items():
|
||||
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
|
||||
# This method is also imperfect for sufficiently complex spends
|
||||
|
@ -21,6 +21,7 @@ from chia.wallet.cat_wallet.cat_utils import (
|
||||
construct_cat_puzzle,
|
||||
unsigned_spend_bundle_for_spendable_cats,
|
||||
)
|
||||
from chia.wallet.conditions import ConditionValidTimes
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.payment import Payment
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
@ -288,6 +289,7 @@ class TestOfferLifecycle:
|
||||
},
|
||||
{"xch": 900, str_to_tail_hash("red").hex(): 350},
|
||||
driver_dict_as_infos,
|
||||
ConditionValidTimes(),
|
||||
)
|
||||
assert new_offer.get_pending_amounts() == {
|
||||
"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_utils import CAT_MOD, construct_cat_puzzle
|
||||
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.did_wallet.did_wallet import DIDWallet
|
||||
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()
|
||||
id, advanced_summary = await wallet_1_rpc.get_offer_summary(offer, advanced=True)
|
||||
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
|
||||
|
||||
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
|
||||
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.asyncio
|
||||
|
Loading…
Reference in New Issue
Block a user