|
9 | 9 |
|
10 | 10 | from chia._tests.clvm.test_custody_architecture import ACSMember |
11 | 11 | from chia._tests.util.spend_sim import CostLogger, sim_and_client |
| 12 | +from chia.pools.plotnft_drivers import SendMessageBanned |
12 | 13 | from chia.types.blockchain_format.program import Program, run |
13 | 14 | from chia.types.coin_spend import make_spend |
14 | 15 | from chia.types.mempool_inclusion_status import MempoolInclusionStatus |
15 | 16 | from chia.util.errors import Err |
16 | | -from chia.wallet.conditions import CreateCoin, Remark, parse_conditions_non_consensus |
| 17 | +from chia.wallet.conditions import CreateCoin, MessageParticipant, Remark, SendMessage, parse_conditions_non_consensus |
17 | 18 | from chia.wallet.puzzles.custody.custody_architecture import ( |
18 | 19 | DelegatedPuzzleAndSolution, |
19 | 20 | MemberOrDPuz, |
@@ -230,6 +231,84 @@ async def test_fixed_create_coin_wrapper(cost_logger: CostLogger) -> None: |
230 | 231 | assert restriction.memo(0) == Program.to([None]) |
231 | 232 |
|
232 | 233 |
|
| 234 | +@pytest.mark.anyio |
| 235 | +async def test_send_message_banned(cost_logger: CostLogger) -> None: |
| 236 | + async with sim_and_client() as (sim, client): |
| 237 | + restriction = ValidatorStackRestriction(required_wrappers=[SendMessageBanned()]) |
| 238 | + pwr = PuzzleWithRestrictions(nonce=0, restrictions=[restriction], puzzle=ACSMember()) |
| 239 | + |
| 240 | + # Farm and find coin |
| 241 | + await sim.farm_block(pwr.puzzle_hash()) |
| 242 | + coin = (await client.get_coin_records_by_puzzle_hashes([pwr.puzzle_hash()], include_spent_coins=False))[0].coin |
| 243 | + |
| 244 | + # Attempt to send a message |
| 245 | + send_message_dpuz = DelegatedPuzzleAndSolution( |
| 246 | + puzzle=Program.to( |
| 247 | + ( |
| 248 | + 1, |
| 249 | + [ |
| 250 | + SendMessage( |
| 251 | + bytes32.zeros, |
| 252 | + sender=MessageParticipant(parent_id_committed=bytes32.zeros), |
| 253 | + receiver=MessageParticipant(parent_id_committed=bytes32.zeros), |
| 254 | + ).to_program() |
| 255 | + ], |
| 256 | + ) |
| 257 | + ), |
| 258 | + solution=Program.to(None), |
| 259 | + ) |
| 260 | + wrapped_dpuz = restriction.modify_delegated_puzzle_and_solution( |
| 261 | + send_message_dpuz, [Program.to(None), Program.to(None)] |
| 262 | + ) |
| 263 | + escape_attempt = WalletSpendBundle( |
| 264 | + [ |
| 265 | + make_spend( |
| 266 | + coin, |
| 267 | + pwr.puzzle_reveal(), |
| 268 | + pwr.solve( |
| 269 | + [], |
| 270 | + [Program.to([send_message_dpuz.puzzle.get_tree_hash()])], |
| 271 | + Program.to(None), |
| 272 | + wrapped_dpuz, |
| 273 | + ), |
| 274 | + ) |
| 275 | + ], |
| 276 | + G2Element(), |
| 277 | + ) |
| 278 | + result = await client.push_tx(escape_attempt) |
| 279 | + assert result == (MempoolInclusionStatus.FAILED, Err.GENERATOR_RUNTIME_ERROR) |
| 280 | + |
| 281 | + # Now send it to the correct place |
| 282 | + self_destruct_dpuz = DelegatedPuzzleAndSolution( |
| 283 | + puzzle=Program.to(None), |
| 284 | + solution=Program.to(None), |
| 285 | + ) |
| 286 | + wrapped_dpuz = restriction.modify_delegated_puzzle_and_solution(self_destruct_dpuz, [Program.to(None)]) |
| 287 | + sb = cost_logger.add_cost( |
| 288 | + "Minimal puzzle with restrictions w/ send message banned wrapper", |
| 289 | + WalletSpendBundle( |
| 290 | + [ |
| 291 | + make_spend( |
| 292 | + coin, |
| 293 | + pwr.puzzle_reveal(), |
| 294 | + pwr.solve( |
| 295 | + [], |
| 296 | + [Program.to([self_destruct_dpuz.puzzle.get_tree_hash()])], |
| 297 | + Program.to(None), |
| 298 | + wrapped_dpuz, |
| 299 | + ), |
| 300 | + ) |
| 301 | + ], |
| 302 | + G2Element(), |
| 303 | + ), |
| 304 | + ) |
| 305 | + result = await client.push_tx(sb) |
| 306 | + assert result == (MempoolInclusionStatus.SUCCESS, None) |
| 307 | + |
| 308 | + # memo format assertion for coverage sake |
| 309 | + assert restriction.memo(0) == Program.to([None]) |
| 310 | + |
| 311 | + |
233 | 312 | @dataclass(frozen=True) |
234 | 313 | class SelfDestructRestriction: |
235 | 314 | @property |
|
0 commit comments