mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-09-27 17:46:28 -04:00
update trade status when farmed
This commit is contained in:
@@ -18,7 +18,6 @@ from src.cmds.init import check_keys
|
||||
from src.server.outbound_message import NodeType, OutboundMessage, Message, Delivery
|
||||
from src.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from src.util.ints import uint64, uint32
|
||||
from src.wallet.trading.trade_status import TradeStatus
|
||||
from src.wallet.util.wallet_types import WalletType
|
||||
from src.wallet.rl_wallet.rl_wallet import RLWallet
|
||||
from src.wallet.cc_wallet.cc_wallet import CCWallet
|
||||
@@ -70,6 +69,11 @@ class WalletRpcApi:
|
||||
}
|
||||
|
||||
async def get_trades(self, request: Dict):
|
||||
if self.service is None:
|
||||
return {"success": False}
|
||||
if self.service.wallet_state_manager is None:
|
||||
return {"success": False}
|
||||
|
||||
all = request["all"]
|
||||
trade_mgr = self.service.wallet_state_manager.trade_manager
|
||||
if all:
|
||||
@@ -85,7 +89,12 @@ class WalletRpcApi:
|
||||
response = {"success": True, "trades": result}
|
||||
return response
|
||||
|
||||
async def cancel_trade(self, request:Dict):
|
||||
async def cancel_trade(self, request: Dict):
|
||||
if self.service is None:
|
||||
return {"success": False}
|
||||
if self.service.wallet_state_manager is None:
|
||||
return {"success": False}
|
||||
|
||||
wsm = self.service.wallet_state_manager
|
||||
secure = request["secure"]
|
||||
trade_id = request["trade_id"]
|
||||
|
||||
@@ -53,11 +53,12 @@ class TradeManager:
|
||||
records = await self.trade_store.get_trade_record_with_status(status)
|
||||
return records
|
||||
|
||||
async def get_coins_of_interest(self):
|
||||
async def get_coins_of_interest(
|
||||
self,
|
||||
) -> Tuple[Dict[bytes32, Coin], Dict[bytes32, Coin]]:
|
||||
"""
|
||||
Returns list of coins we want to monitor blockchain for,
|
||||
Those will be both coins belonging to us, and coins belonging to someone else that we tried to use in trade
|
||||
This is important as we might not be the only one who tries to execute that trade.
|
||||
Returns list of coins we want to check if they are included in filter,
|
||||
These will include coins that belong to us and coins that that on other side of treade
|
||||
"""
|
||||
all_pending = []
|
||||
pending_accept = await self.get_offers_with_status(TradeStatus.PENDING_ACCEPT)
|
||||
@@ -66,13 +67,94 @@ class TradeManager:
|
||||
all_pending.extend(pending_accept)
|
||||
all_pending.extend(pending_confirm)
|
||||
all_pending.extend(pending_cancel)
|
||||
result = {}
|
||||
removals = {}
|
||||
additions = {}
|
||||
|
||||
for trade in all_pending:
|
||||
for coin in trade.spend_bundle.removals():
|
||||
result[coin.name()] = coin
|
||||
removals[coin.name()] = coin
|
||||
for coin in trade.spend_bundle.additions():
|
||||
additions[coin.name()] = coin
|
||||
|
||||
return result
|
||||
return removals, additions
|
||||
|
||||
async def get_trade_by_coin(self, coin: Coin) -> Optional[TradeRecord]:
|
||||
all_trades = await self.get_all_trades()
|
||||
for trade in all_trades:
|
||||
if coin in trade.removals:
|
||||
return trade
|
||||
if coin in trade.additions:
|
||||
return trade
|
||||
return None
|
||||
|
||||
async def coins_of_interest_farmed(
|
||||
self, removals: List[Coin], additions: List[Coin], index: uint32
|
||||
):
|
||||
"""
|
||||
If both our coins and other coins in trade got removed that means that trade was successfully executed
|
||||
If coins from other side of trade got farmed without ours, that means that trade failed because either someone
|
||||
else completed trade or other side of trade canceled the trade by doing a spend.
|
||||
If our coins got farmed but coins from other side didn't, we successfully canceled trade by spending inputs.
|
||||
"""
|
||||
removal_dict = {}
|
||||
addition_dict = {}
|
||||
checked: Dict[bytes32, Coin] = {}
|
||||
for coin in removals:
|
||||
removal_dict[coin.name()] = coin
|
||||
for coin in additions:
|
||||
addition_dict[coin.name()] = coin
|
||||
|
||||
all_coins = []
|
||||
all_coins.extend(removals)
|
||||
all_coins.extend(additions)
|
||||
|
||||
for coin in all_coins:
|
||||
if coin.name() in checked:
|
||||
continue
|
||||
trade = await self.get_trade_by_coin(coin)
|
||||
if trade is None:
|
||||
self.log.error(f"Coin: {Coin}, not in any trade")
|
||||
continue
|
||||
|
||||
# Check if all coins that are part of the trade got farmed
|
||||
# If coin is missing, trade failed
|
||||
failed = False
|
||||
for coin in trade.removals:
|
||||
if coin.name() not in removal_dict:
|
||||
self.log.error(f"{coin} from trade not removed")
|
||||
failed = True
|
||||
checked[coin.name()] = coin
|
||||
for coin in trade.additions:
|
||||
if coin.name() not in addition_dict:
|
||||
self.log.error(f"{coin} from trade not added")
|
||||
failed = True
|
||||
checked[coin.name()] = coin
|
||||
|
||||
if failed is False:
|
||||
# Mark this trade as succesfull
|
||||
await self.trade_store.set_status(
|
||||
trade.trade_id, TradeStatus.CONFIRMED, index
|
||||
)
|
||||
self.log.info(
|
||||
f"Trade with id: {trade.trade_id} confirmed at height: {index}"
|
||||
)
|
||||
else:
|
||||
# Either we canceled this trade or this trade failed
|
||||
status = TradeStatus(trade.status)
|
||||
if status is TradeStatus.PENDING_CANCEL:
|
||||
await self.trade_store.set_status(
|
||||
trade.trade_id, TradeStatus.CANCELED
|
||||
)
|
||||
self.log.info(
|
||||
f"Trade with id: {trade.trade_id} canceled at height: {index}"
|
||||
)
|
||||
else:
|
||||
await self.trade_store.set_status(
|
||||
trade.trade_id, TradeStatus.FAILED
|
||||
)
|
||||
self.log.warning(
|
||||
f"Trade with id: {trade.trade_id} failed at height: {index}"
|
||||
)
|
||||
|
||||
async def get_locked_coins(
|
||||
self, wallet_id: int = None
|
||||
|
||||
@@ -37,5 +37,5 @@ class TradeRecord(Streamable):
|
||||
result["created_at_time"] = self.created_at_time
|
||||
result["accepted_at_time"] = self.accepted_at_time
|
||||
result["confirmed_at_index"] = self.confirmed_at_index
|
||||
result["status"] = TradeStatus(self.status).name
|
||||
result["status"] = TradeStatus(self.status).name
|
||||
return result
|
||||
|
||||
@@ -7,3 +7,4 @@ class TradeStatus(Enum):
|
||||
PENDING_CANCEL = 2
|
||||
CANCELED = 3
|
||||
CONFIRMED = 4
|
||||
FAILED = 5
|
||||
|
||||
@@ -446,14 +446,69 @@ class WalletStateManager:
|
||||
removals[coin.name()] = coin
|
||||
return removals
|
||||
|
||||
async def coin_removed(self, coin: Coin, index: uint32):
|
||||
async def coins_of_interest_received(
|
||||
self, removals: List[Coin], additions: List[Coin], height: uint32
|
||||
):
|
||||
trade_removals = await self.coins_of_interest_removed(removals, height)
|
||||
trade_additions = await self.coins_of_interest_added(additions, height)
|
||||
if len(trade_additions) > 0 or len(trade_removals) > 0:
|
||||
await self.trade_manager.coins_of_interest_farmed(
|
||||
trade_removals, trade_additions, height
|
||||
)
|
||||
|
||||
async def coins_of_interest_added(
|
||||
self, coins: List[Coin], height: uint32
|
||||
) -> List[Coin]:
|
||||
(
|
||||
trade_removals,
|
||||
trade_additions,
|
||||
) = await self.trade_manager.get_coins_of_interest()
|
||||
trade_adds: List[Coin] = []
|
||||
for coin in coins:
|
||||
if coin.name() in trade_additions:
|
||||
trade_adds.append(coin)
|
||||
|
||||
is_coinbase = False
|
||||
|
||||
if (
|
||||
bytes32((height).to_bytes(32, "big")) == coin.parent_coin_info
|
||||
or std_hash(std_hash(height)) == coin.parent_coin_info
|
||||
):
|
||||
is_coinbase = True
|
||||
|
||||
info = await self.puzzle_store.wallet_info_for_puzzle_hash(coin.puzzle_hash)
|
||||
if info is not None:
|
||||
wallet_id, wallet_type = info
|
||||
await self.coin_added(coin, height, is_coinbase, uint32(wallet_id), wallet_type)
|
||||
|
||||
return trade_adds
|
||||
|
||||
async def coins_of_interest_removed(
|
||||
self, coins: List[Coin], height: uint32
|
||||
) -> List[Coin]:
|
||||
"This get's called when coins of our interest are spent on chain"
|
||||
(
|
||||
trade_removals,
|
||||
trade_additions,
|
||||
) = await self.trade_manager.get_coins_of_interest()
|
||||
|
||||
# Keep track of trade coins that are removed
|
||||
trade_coin_removed: List[Coin] = []
|
||||
|
||||
for coin in coins:
|
||||
record = await self.wallet_store.get_coin_record_by_coin_id(coin.name())
|
||||
if coin.name() in trade_removals:
|
||||
trade_coin_removed.append(coin)
|
||||
if record is None:
|
||||
continue
|
||||
await self.coin_removed(coin, height, record.wallet_id)
|
||||
|
||||
return trade_coin_removed
|
||||
|
||||
async def coin_removed(self, coin: Coin, index: uint32, wallet_id: int):
|
||||
"""
|
||||
Called when coin gets spent
|
||||
"""
|
||||
# Only remove our coins
|
||||
record = await self.wallet_store.get_coin_record_by_coin_id(coin.name())
|
||||
if record is None:
|
||||
return
|
||||
|
||||
await self.wallet_store.set_spent(coin.name(), index)
|
||||
|
||||
@@ -463,15 +518,19 @@ class WalletStateManager:
|
||||
for unconfirmed in unconfirmed_record:
|
||||
await self.tx_store.set_confirmed(unconfirmed.name(), index)
|
||||
|
||||
self.state_changed("coin_removed", record.wallet_id)
|
||||
self.state_changed("coin_removed", wallet_id)
|
||||
|
||||
async def coin_added(self, coin: Coin, index: uint32, coinbase: bool):
|
||||
async def coin_added(
|
||||
self,
|
||||
coin: Coin,
|
||||
index: uint32,
|
||||
coinbase: bool,
|
||||
wallet_id: uint32,
|
||||
wallet_type: WalletType,
|
||||
):
|
||||
"""
|
||||
Adding coin to the db
|
||||
Adding coin to DB
|
||||
"""
|
||||
info = await self.puzzle_store.wallet_info_for_puzzle_hash(coin.puzzle_hash)
|
||||
assert info is not None
|
||||
wallet_id, wallet_type = info
|
||||
if coinbase:
|
||||
now = uint64(int(time.time()))
|
||||
tx_record = TransactionRecord(
|
||||
@@ -671,17 +730,9 @@ class WalletStateManager:
|
||||
assert block.height == 0
|
||||
await self.wallet_store.add_block_to_path(block.header_hash)
|
||||
self.lca = block.header_hash
|
||||
for coin in block.additions:
|
||||
is_coinbase = False
|
||||
if (
|
||||
bytes32((block.height).to_bytes(32, "big"))
|
||||
== coin.parent_coin_info
|
||||
or std_hash(std_hash(block.height)) == coin.parent_coin_info
|
||||
):
|
||||
is_coinbase = True
|
||||
await self.coin_added(coin, block.height, is_coinbase)
|
||||
for coin in block.removals:
|
||||
await self.coin_removed(coin, block.height)
|
||||
await self.coins_of_interest_received(
|
||||
block.removals, block.additions, block.height
|
||||
)
|
||||
self.height_to_hash[uint32(0)] = block.header_hash
|
||||
return ReceiveBlockResult.ADDED_TO_HEAD
|
||||
|
||||
@@ -712,20 +763,9 @@ class WalletStateManager:
|
||||
path_block.additions is not None
|
||||
and path_block.removals is not None
|
||||
)
|
||||
for coin in path_block.additions:
|
||||
is_coinbase = False
|
||||
if (
|
||||
bytes32((path_block.height).to_bytes(32, "big"))
|
||||
== coin.parent_coin_info
|
||||
or std_hash(std_hash(path_block.height.to_bytes(4, "big")))
|
||||
== coin.parent_coin_info
|
||||
):
|
||||
is_coinbase = True
|
||||
|
||||
await self.coin_added(coin, path_block.height, is_coinbase)
|
||||
for coin in path_block.removals:
|
||||
await self.coin_removed(coin, path_block.height)
|
||||
|
||||
await self.coins_of_interest_received(
|
||||
path_block.removals, path_block.additions, path_block.height
|
||||
)
|
||||
self.lca = block.header_hash
|
||||
self.state_changed("new_block")
|
||||
return ReceiveBlockResult.ADDED_TO_HEAD
|
||||
@@ -1162,6 +1202,18 @@ class WalletStateManager:
|
||||
removals_of_interest: bytes32 = []
|
||||
additions_of_interest: bytes32 = []
|
||||
|
||||
(
|
||||
trade_removals,
|
||||
trade_additions,
|
||||
) = await self.trade_manager.get_coins_of_interest()
|
||||
for name, trade_coin in trade_removals.items():
|
||||
if tx_filter.Match(bytearray(trade_coin.name())):
|
||||
removals_of_interest.append(trade_coin.name())
|
||||
|
||||
for name, trade_coin in trade_additions.items():
|
||||
if tx_filter.Match(bytearray(trade_coin.puzzle_hash)):
|
||||
additions_of_interest.append(trade_coin.puzzle_hash)
|
||||
|
||||
for coin_name in unspent_coin_names:
|
||||
if tx_filter.Match(bytearray(coin_name)):
|
||||
removals_of_interest.append(coin_name)
|
||||
|
||||
Reference in New Issue
Block a user