Skip to content

Commit

Permalink
handling stray dao_cats for wallet resync
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffwalmsley committed Dec 9, 2023
1 parent d55c8a5 commit 5434aa0
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 1 deletion.
16 changes: 15 additions & 1 deletion chia/wallet/wallet_state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ async def determine_coin_type(
# Check if the coin is a DAO CAT
dao_cat_args = match_dao_cat_puzzle(uncurried)
if dao_cat_args:
return await self.handle_dao_cat(dao_cat_args, parent_coin_state, coin_state, coin_spend), None
return await self.handle_dao_cat(dao_cat_args, parent_coin_state, coin_state, coin_spend, fork_height), None

# Check if the coin is a CAT
cat_curried_args = match_cat_puzzle(uncurried)
Expand Down Expand Up @@ -1021,6 +1021,7 @@ async def handle_dao_cat(
parent_coin_state: CoinState,
coin_state: CoinState,
coin_spend: CoinSpend,
fork_height: Optional[uint32],
) -> Optional[WalletIdentifier]:
"""
Handle the new coin when it is a DAO CAT
Expand All @@ -1032,6 +1033,19 @@ async def handle_dao_cat(
assert isinstance(wallet, DAOCATWallet)
if wallet.dao_cat_info.limitations_program_hash == asset_id:
return WalletIdentifier.create(wallet)
# Found a DAO_CAT, but we don't have a wallet for it. Add to unacknowledged
await self.interested_store.add_unacknowledged_token(
asset_id,
CATWallet.default_wallet_name_for_unknown_cat(asset_id.hex()),
None if parent_coin_state.spent_height is None else uint32(parent_coin_state.spent_height),
parent_coin_state.coin.puzzle_hash,
)
await self.interested_store.add_unacknowledged_coin_state(
asset_id,
coin_state,
fork_height,
)
self.state_changed("added_stray_cat")
return None # pragma: no cover

async def handle_cat(
Expand Down
187 changes: 187 additions & 0 deletions tests/wallet/dao_wallet/test_dao_wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3348,3 +3348,190 @@ async def test_dao_votes(
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

assert dao_wallet_0.dao_info.proposals_list[3].amount_voted == 210000


@pytest.mark.limit_consensus_modes(reason="does not depend on consensus rules")
@pytest.mark.parametrize(
"trusted",
[True, False],
)
@pytest.mark.anyio
async def test_dao_resync(
self_hostname: str, two_wallet_nodes: SimulatorsAndWallets, trusted: bool, consensus_mode: ConsensusMode
) -> None:
full_nodes, wallets, _ = two_wallet_nodes
full_node_api = full_nodes[0]
full_node_server = full_node_api.server
wallet_node_0, server_0 = wallets[0]
wallet_node_1, server_1 = wallets[1]
wallet_api_0 = WalletRpcApi(wallet_node_0)
wallet_api_1 = WalletRpcApi(wallet_node_1)
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
ph = await wallet_0.get_new_puzzlehash()
ph_1 = await wallet_1.get_new_puzzlehash()

if trusted:
wallet_node_0.config["trusted_peers"] = {
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
}
wallet_node_1.config["trusted_peers"] = {
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
}
else:
wallet_node_0.config["trusted_peers"] = {}
wallet_node_1.config["trusted_peers"] = {}

await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)

await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph_1))

funds = calculate_pool_reward(uint32(1)) + calculate_base_farmer_reward(uint32(1))

await time_out_assert(20, wallet_0.get_confirmed_balance, funds)
await time_out_assert(20, full_node_api.wallet_is_synced, True, wallet_node_0)

cat_amt = 2000
dao_rules = DAORules(
proposal_timelock=uint64(10),
soft_close_length=uint64(5),
attendance_required=uint64(1000), # 10%
pass_percentage=uint64(5100), # 51%
self_destruct_length=uint64(20),
oracle_spend_delay=uint64(10),
proposal_minimum_amount=uint64(1),
)

fee = uint64(10)
fee_for_cat = uint64(20)
dao_wallet_0 = await DAOWallet.create_new_dao_and_wallet(
wallet_node_0.wallet_state_manager,
wallet_0,
uint64(cat_amt * 2),
dao_rules,
DEFAULT_TX_CONFIG,
fee=fee,
fee_for_cat=fee_for_cat,
)
assert dao_wallet_0 is not None

txs = await wallet_0.wallet_state_manager.tx_store.get_all_unconfirmed()
await full_node_api.wait_transaction_records_entered_mempool(records=txs, timeout=60)
await full_node_api.process_all_wallet_transactions(wallet_0, timeout=60)
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=30)

treasury_id = dao_wallet_0.dao_info.treasury_id

# get the cat wallets
cat_wallet_0 = dao_wallet_0.wallet_state_manager.wallets[dao_wallet_0.dao_info.cat_wallet_id]
# dao_cat_wallet_0 = dao_wallet_0.wallet_state_manager.wallets[dao_wallet_0.dao_info.dao_cat_wallet_id]

# Create the other user's wallet from the treasury id
dao_wallet_1 = await DAOWallet.create_new_dao_wallet_for_existing_dao(
wallet_node_1.wallet_state_manager,
wallet_1,
treasury_id,
)
assert dao_wallet_1 is not None
assert dao_wallet_0.dao_info.treasury_id == dao_wallet_1.dao_info.treasury_id

# Get the cat wallets for wallet_1
cat_wallet_1 = dao_wallet_1.wallet_state_manager.wallets[dao_wallet_1.dao_info.cat_wallet_id]

# Send some cats to the dao_cat lockup
dao_cat_amt = uint64(100)
txs = await dao_wallet_0.enter_dao_cat_voting_mode(dao_cat_amt, DEFAULT_TX_CONFIG)
for tx in txs:
await wallet_0.wallet_state_manager.add_pending_transaction(tx)

await full_node_api.wait_transaction_records_entered_mempool(records=txs, timeout=60)
await full_node_api.process_all_wallet_transactions(wallet_0, timeout=60)
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

# send some cats from wallet_0 to wallet_1 so we can test voting
cat_txs = await cat_wallet_0.generate_signed_transaction([cat_amt], [ph_1], DEFAULT_TX_CONFIG)
await wallet_0.wallet_state_manager.add_pending_transaction(cat_txs[0])

await full_node_api.wait_transaction_records_entered_mempool(records=cat_txs, timeout=60)
await full_node_api.process_all_wallet_transactions(wallet_0, timeout=60)
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)

await time_out_assert(10, cat_wallet_1.get_confirmed_balance, cat_amt)

recipient_puzzle_hash = await wallet_1.get_new_puzzlehash()
proposal_amount_1 = uint64(9998)
xch_proposal_inner = generate_simple_proposal_innerpuz(
treasury_id,
[recipient_puzzle_hash],
[proposal_amount_1],
[None],
)
proposal_tx = await dao_wallet_0.generate_new_proposal(xch_proposal_inner, DEFAULT_TX_CONFIG, uint64(10))
await wallet_0.wallet_state_manager.add_pending_transaction(proposal_tx)
await full_node_api.wait_transaction_records_entered_mempool(records=[proposal_tx], timeout=60)
await full_node_api.process_all_wallet_transactions(wallet_0, timeout=60)
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=30)

# make another proposal spending all the dao_cats
xch_proposal_inner = generate_simple_proposal_innerpuz(
treasury_id,
[recipient_puzzle_hash],
[proposal_amount_1],
[None],
)
proposal_tx = await dao_wallet_0.generate_new_proposal(xch_proposal_inner, DEFAULT_TX_CONFIG)
await wallet_0.wallet_state_manager.add_pending_transaction(proposal_tx)
await full_node_api.wait_transaction_records_entered_mempool(records=[proposal_tx], timeout=60)
await full_node_api.process_all_wallet_transactions(wallet_0, timeout=60)
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=30)

# set flag to reset wallet sync data on start
await wallet_api_0.set_wallet_resync_on_startup({"enable": True})
fingerprint_0 = wallet_node_0.logged_in_fingerprint
await wallet_api_1.set_wallet_resync_on_startup({"enable": True})
fingerprint_1 = wallet_node_1.logged_in_fingerprint
# Delete tx records
await wallet_node_0.wallet_state_manager.tx_store.rollback_to_block(0)
wallet_node_0._close()
await wallet_node_0._await_closed()
wallet_node_1._close()
await wallet_node_1._await_closed()
wallet_node_0.config["database_path"] = "wallet/db/blockchain_wallet_v2_test_1_CHALLENGE_KEY.sqlite"
wallet_node_1.config["database_path"] = "wallet/db/blockchain_wallet_v2_test_2_CHALLENGE_KEY.sqlite"
# Start resync
await wallet_node_0._start_with_fingerprint(fingerprint_0)
await wallet_node_1._start_with_fingerprint(fingerprint_1)
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash_0))
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_1, timeout=20)
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet

assert len(await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries()) == 1

new_dao_wallet = await DAOWallet.create_new_dao_wallet_for_existing_dao(
wallet_node_0.wallet_state_manager,
wallet_0,
treasury_id,
)

await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash_0))
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
assert len(await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries()) == 4
new_cat_wallet = new_dao_wallet.wallet_state_manager.wallets[new_dao_wallet.dao_info.cat_wallet_id]
new_dao_cat_wallet = new_dao_wallet.wallet_state_manager.wallets[new_dao_wallet.dao_info.dao_cat_wallet_id]

await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash_0))
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)

# Check the new wallets have the right balances
await time_out_assert(20, new_cat_wallet.get_confirmed_balance, cat_amt - dao_cat_amt)
await time_out_assert(20, new_dao_cat_wallet.get_confirmed_balance, dao_cat_amt)

# Check the proposals are found and accurate
assert len(new_dao_wallet.dao_info.proposals_list) == 2
assert new_dao_wallet.dao_info.proposals_list[0].yes_votes == 10
assert new_dao_wallet.dao_info.proposals_list[1].yes_votes == 100

0 comments on commit 5434aa0

Please sign in to comment.