create-dubhe 1.2.0-pre.124 → 1.2.0-pre.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/101/sui-template/packages/contracts/src/dubhe/Move.lock +0 -6
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/contract/sui-template/src/dubhe/Move.lock +0 -6
- package/templates/contract/sui-template/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/contract/sui-template/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/nextjs/sui-card-duel/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/README.md +213 -0
- package/templates/nextjs/sui-card-duel/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/mprocs.yaml +17 -0
- package/templates/nextjs/sui-card-duel/package.json +43 -0
- package/templates/nextjs/sui-card-duel/packages/client/.gitignore_ +48 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/eslint.config.mjs +22 -0
- package/templates/nextjs/sui-card-duel/packages/client/next.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/package.json +47 -0
- package/templates/nextjs/sui-card-duel/packages/client/postcss.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/ArenaPanel.tsx +307 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/page.tsx +622 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/brawl/[id]/page.tsx +559 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/collection/page.tsx +202 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/CardView.tsx +66 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/GameShell.tsx +294 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/icons.tsx +101 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/duel/[id]/page.tsx +477 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/favicon.ico +0 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/globals.css +85 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/hooks/useGame.ts +313 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/layout.tsx +24 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/leaderboard/page.tsx +123 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/game.ts +77 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/scenes.ts +226 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/market/page.tsx +418 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/page.tsx +427 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/providers.tsx +69 -0
- package/templates/nextjs/sui-card-duel/packages/client/tailwind.config.ts +21 -0
- package/templates/nextjs/sui-card-duel/packages/client/tsconfig.json +30 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Dockerfile +5 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Pub.localnet.toml +13 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/card_duel.lock.json +66 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/dubhe.config.ts +150 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/logs/combined.log +31383 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/package.json +49 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/config.ts +17 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-brawl.ts +270 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-duel.ts +272 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-lobby.ts +177 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-market.ts +219 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-session.ts +342 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/seed.ts +92 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/Move.toml +15 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/dapp_key.move +46 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/card_kind.move +47 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/match_state.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/rarity.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/error.move +126 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/genesis.move +33 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/init_test.move +23 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/objects/arena.move +137 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/brawl_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/duel_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/battle_state.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/card.move +282 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/deck.move +64 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/game_config.move +267 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/gold.move +205 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/profile.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/brawl.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/duel.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/deploy_hook.move +32 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/migrate.move +7 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/arena_system.move +57 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/brawl_system.move +559 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/card_system.move +146 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/duel_system.move +515 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/market_system.move +115 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/player_system.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/brawl_tests.move +385 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/duel_tests.move +656 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/test_helpers.move +31 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/sui-indexer-schema.graphql +5973 -0
- package/templates/nextjs/sui-card-duel/pnpm-workspace.yaml +2 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/market/page.tsx +4 -13
- package/templates/nextjs/sui-farm/packages/client/src/app/page.tsx +73 -130
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/permits/world.move +12 -9
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/world_system.move +1 -1
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +0 -6
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/nextjs/sui-farm/packages/client/src/app/hooks/useSessionKey.ts +0 -248
package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move
CHANGED
|
@@ -310,13 +310,15 @@ fun test_accept_invitation_moves_invitee_to_participants() {
|
|
|
310
310
|
assert!(!dapp_service::is_participant_in_scene_permit(&permit, alice), 0);
|
|
311
311
|
assert!(dapp_service::is_scene_invitee(dapp_service::scene_permit_meta(&permit), alice), 1);
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
let us_alice = make_us(alice, ctx);
|
|
314
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
314
315
|
|
|
315
316
|
assert!(dapp_service::is_participant_in_scene_permit(&permit, alice), 2);
|
|
316
317
|
assert!(!dapp_service::is_scene_invitee(dapp_service::scene_permit_meta(&permit), alice), 3);
|
|
317
318
|
assert!(dapp_service::is_scene_invitee(dapp_service::scene_permit_meta(&permit), bob), 4);
|
|
318
319
|
assert!(!dapp_service::is_participant_in_scene_permit(&permit, bob), 5);
|
|
319
320
|
|
|
321
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
320
322
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
321
323
|
}
|
|
322
324
|
|
|
@@ -332,9 +334,11 @@ fun test_accept_invitation_with_expiry_in_window() {
|
|
|
332
334
|
ctx,
|
|
333
335
|
);
|
|
334
336
|
|
|
335
|
-
|
|
337
|
+
let us_alice = make_us(alice, ctx);
|
|
338
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
336
339
|
assert!(dapp_service::is_participant_in_scene_permit(&permit, alice), 0);
|
|
337
340
|
|
|
341
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
338
342
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
339
343
|
}
|
|
340
344
|
|
|
@@ -351,7 +355,9 @@ fun test_accept_invitation_expired_aborts() {
|
|
|
351
355
|
ctx,
|
|
352
356
|
);
|
|
353
357
|
|
|
354
|
-
|
|
358
|
+
let us_alice = make_us(alice, ctx);
|
|
359
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
360
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
355
361
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
356
362
|
}
|
|
357
363
|
|
|
@@ -368,7 +374,9 @@ fun test_accept_invitation_not_invited_aborts() {
|
|
|
368
374
|
ctx,
|
|
369
375
|
);
|
|
370
376
|
|
|
371
|
-
|
|
377
|
+
let us_charlie = make_us(charlie, ctx);
|
|
378
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_charlie, ctx);
|
|
379
|
+
dapp_service::destroy_user_storage(us_charlie);
|
|
372
380
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
373
381
|
}
|
|
374
382
|
|
|
@@ -385,16 +393,20 @@ fun test_all_invitees_accept_flow() {
|
|
|
385
393
|
ctx_a,
|
|
386
394
|
);
|
|
387
395
|
|
|
388
|
-
|
|
396
|
+
let us_alice = make_us(alice, ctx_a);
|
|
397
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx_a);
|
|
389
398
|
|
|
390
399
|
let ctx_b = &mut tx_context::new_from_hint(bob, 0, 0, 0, 0);
|
|
391
|
-
|
|
400
|
+
let us_bob = make_us(bob, ctx_b);
|
|
401
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_bob, ctx_b);
|
|
392
402
|
|
|
393
403
|
assert!(dapp_service::is_participant_in_scene_permit(&permit, alice), 0);
|
|
394
404
|
assert!(dapp_service::is_participant_in_scene_permit(&permit, bob), 1);
|
|
395
405
|
assert!(dapp_service::scene_invitees(dapp_service::scene_permit_meta(&permit)).is_empty(), 2);
|
|
396
406
|
assert!(dapp_service::scene_participant_count(dapp_service::scene_permit_meta(&permit)) == 2, 3);
|
|
397
407
|
|
|
408
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
409
|
+
dapp_service::destroy_user_storage(us_bob);
|
|
398
410
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
399
411
|
}
|
|
400
412
|
|
|
@@ -411,8 +423,10 @@ fun test_accept_invitation_twice_aborts() {
|
|
|
411
423
|
ctx,
|
|
412
424
|
);
|
|
413
425
|
|
|
414
|
-
|
|
415
|
-
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, ctx);
|
|
426
|
+
let us_alice = make_us(alice, ctx);
|
|
427
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
428
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
429
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
416
430
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
417
431
|
}
|
|
418
432
|
|
|
@@ -430,7 +444,9 @@ fun test_accept_invitation_on_expired_scene_aborts() {
|
|
|
430
444
|
ctx,
|
|
431
445
|
);
|
|
432
446
|
|
|
433
|
-
|
|
447
|
+
let us_alice = make_us(alice, ctx);
|
|
448
|
+
dapp_system::accept_scene_permit_invitation<SceneKey, SceneKey>(SceneKey {}, &mut permit, &us_alice, ctx);
|
|
449
|
+
dapp_service::destroy_user_storage(us_alice);
|
|
434
450
|
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
435
451
|
}
|
|
436
452
|
|
|
@@ -39,9 +39,3 @@ source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-f
|
|
|
39
39
|
use_environment = "testnet"
|
|
40
40
|
manifest_digest = "7AFB66695545775FBFBB2D3078ADFD084244D5002392E837FDE21D9EA1C6D01C"
|
|
41
41
|
deps = { MoveStdlib = "MoveStdlib" }
|
|
42
|
-
|
|
43
|
-
[env.mainnet]
|
|
44
|
-
chain-id = "35834a8a"
|
|
45
|
-
original-published-id = "0x6cb54e8bbcf59cc976584330689073500af755b12d85bf4f56d7eea9f232b8aa"
|
|
46
|
-
latest-published-id = "0x6cb54e8bbcf59cc976584330689073500af755b12d85bf4f56d7eea9f232b8aa"
|
|
47
|
-
published-version = "1"
|
|
@@ -278,9 +278,10 @@ public fun delete_field<DappKey: copy + drop>(
|
|
|
278
278
|
//
|
|
279
279
|
// Reactive writes allow one participant to modify another participant's UserStorage
|
|
280
280
|
// within a shared scene context. Four-layer security:
|
|
281
|
-
// 1. ctx.sender() must be from
|
|
282
|
-
//
|
|
283
|
-
//
|
|
281
|
+
// 1. ctx.sender() must be authorized to write `from` (canonical owner or its
|
|
282
|
+
// active session key — sessions are delegated proxies for in-scene actions)
|
|
283
|
+
// 2. from's canonical owner must be a registered scene participant
|
|
284
|
+
// 3. target's canonical owner must be a registered scene participant
|
|
284
285
|
// 4. scene must be active (not expired)
|
|
285
286
|
//
|
|
286
287
|
// Write fees are charged to the initiator (`from`) under the initiator-pays model.
|
|
@@ -304,8 +305,8 @@ public fun set_record_reactive<DappKey: copy + drop, PermType>(
|
|
|
304
305
|
let scene_id = dapp_service::scene_permit_id(permit);
|
|
305
306
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
306
307
|
|
|
307
|
-
// 1. Sender must be
|
|
308
|
-
error::
|
|
308
|
+
// 1. Sender must be authorized to write `from` (canonical owner or active session key).
|
|
309
|
+
error::no_permission(dapp_service::is_write_authorized(from, ctx.sender(), ctx.epoch_timestamp_ms()));
|
|
309
310
|
// 2. Initiator must be a scene participant (O(1) DF lookup).
|
|
310
311
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(from)));
|
|
311
312
|
// 3. Target must be a scene participant (O(1) DF lookup).
|
|
@@ -343,7 +344,7 @@ public fun set_field_reactive<DappKey: copy + drop, PermType>(
|
|
|
343
344
|
let scene_id = dapp_service::scene_permit_id(permit);
|
|
344
345
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
345
346
|
|
|
346
|
-
error::
|
|
347
|
+
error::no_permission(dapp_service::is_write_authorized(from, ctx.sender(), ctx.epoch_timestamp_ms()));
|
|
347
348
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(from)));
|
|
348
349
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(target)));
|
|
349
350
|
error::scene_expired(dapp_service::is_scene_active(meta, ctx.epoch_timestamp_ms()));
|
|
@@ -504,19 +505,27 @@ public fun remove_object_field<DappKey: copy + drop, ObjType, T: store + copy +
|
|
|
504
505
|
|
|
505
506
|
/// Remove and return a native-typed field from a permit-bound SceneStorage Bag.
|
|
506
507
|
/// Emits a Dubhe_Scene_DeleteField event so off-chain indexers stay in sync.
|
|
508
|
+
/// The caller identifies as `user_storage`'s canonical owner; the transaction may be
|
|
509
|
+
/// signed by the canonical owner or its active session key (is_write_authorized).
|
|
507
510
|
public fun remove_scene_field<DappKey: copy + drop, PermType, SceneType, T: store + copy + drop>(
|
|
508
|
-
_auth:
|
|
509
|
-
permit:
|
|
510
|
-
storage:
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
_auth: DappKey,
|
|
512
|
+
permit: &ScenePermit<PermType>,
|
|
513
|
+
storage: &mut SceneStorage<SceneType>,
|
|
514
|
+
user_storage: &UserStorage,
|
|
515
|
+
field_name: vector<u8>,
|
|
516
|
+
ctx: &TxContext,
|
|
513
517
|
): T {
|
|
514
518
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
515
519
|
error::dapp_key_mismatch(dapp_service::scene_storage_dapp_key(storage) == dapp_key_str);
|
|
516
520
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
521
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
517
522
|
assert_scene_storage_bound_to_permit(permit, storage);
|
|
518
|
-
|
|
519
|
-
error::
|
|
523
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
524
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
525
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
526
|
+
error::not_scene_participant(dapp_service::is_participant_in_scene_permit(
|
|
527
|
+
permit, dapp_service::canonical_owner(user_storage)
|
|
528
|
+
));
|
|
520
529
|
|
|
521
530
|
let scene_type = *dapp_service::scene_storage_type(storage);
|
|
522
531
|
let scene_id = sui::object::uid_to_address(dapp_service::scene_storage_id(storage));
|
|
@@ -729,20 +738,28 @@ public fun share_scene_storage<DappKey: copy + drop, SceneType>(
|
|
|
729
738
|
}
|
|
730
739
|
|
|
731
740
|
/// Write a native-typed field into a permit-bound SceneStorage Bag and emit an event.
|
|
741
|
+
/// The caller identifies as `user_storage`'s canonical owner; the transaction may be
|
|
742
|
+
/// signed by the canonical owner or its active session key (is_write_authorized).
|
|
732
743
|
public fun set_scene_field<DappKey: copy + drop, PermType, SceneType, T: store + copy + drop>(
|
|
733
|
-
_auth:
|
|
734
|
-
permit:
|
|
735
|
-
storage:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
744
|
+
_auth: DappKey,
|
|
745
|
+
permit: &ScenePermit<PermType>,
|
|
746
|
+
storage: &mut SceneStorage<SceneType>,
|
|
747
|
+
user_storage: &UserStorage,
|
|
748
|
+
field_name: vector<u8>,
|
|
749
|
+
value: T,
|
|
750
|
+
ctx: &TxContext,
|
|
739
751
|
) {
|
|
740
752
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
741
753
|
error::dapp_key_mismatch(dapp_service::scene_storage_dapp_key(storage) == dapp_key_str);
|
|
742
754
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
755
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
743
756
|
assert_scene_storage_bound_to_permit(permit, storage);
|
|
744
|
-
|
|
745
|
-
error::
|
|
757
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
758
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
759
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
760
|
+
error::not_scene_participant(dapp_service::is_participant_in_scene_permit(
|
|
761
|
+
permit, dapp_service::canonical_owner(user_storage)
|
|
762
|
+
));
|
|
746
763
|
|
|
747
764
|
let event_bytes = sui::bcs::to_bytes(&value);
|
|
748
765
|
dapp_service::set_scene_field(storage, field_name, value);
|
|
@@ -829,19 +846,25 @@ public fun destroy_scene_permit<DappKey: copy + drop, PermType>(
|
|
|
829
846
|
}
|
|
830
847
|
|
|
831
848
|
/// Helper: accept a scene invitation for a ScenePermit-backed scene.
|
|
832
|
-
/// Moves
|
|
849
|
+
/// Moves the caller's canonical owner from the invitees list to confirmed
|
|
850
|
+
/// participants. The transaction may be signed by the canonical owner or its
|
|
851
|
+
/// active session key (is_write_authorized) — the registered identity is
|
|
852
|
+
/// always the canonical owner address.
|
|
833
853
|
/// Guards: scene must be active AND the invitation window must not have expired.
|
|
834
854
|
public fun accept_scene_permit_invitation<DappKey: copy + drop, PermType>(
|
|
835
|
-
_auth:
|
|
836
|
-
permit:
|
|
837
|
-
|
|
855
|
+
_auth: DappKey,
|
|
856
|
+
permit: &mut ScenePermit<PermType>,
|
|
857
|
+
user_storage: &UserStorage,
|
|
858
|
+
ctx: &TxContext,
|
|
838
859
|
) {
|
|
839
860
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
840
861
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
862
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
841
863
|
|
|
842
864
|
// Check both permit activity and invitation window before mutating.
|
|
843
865
|
// The immutable borrow of `meta` is released at the end of this block.
|
|
844
866
|
let now_ms = ctx.epoch_timestamp_ms();
|
|
867
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
845
868
|
{
|
|
846
869
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
847
870
|
error::scene_expired(dapp_service::is_scene_active(meta, now_ms));
|
|
@@ -851,54 +874,71 @@ public fun accept_scene_permit_invitation<DappKey: copy + drop, PermType>(
|
|
|
851
874
|
};
|
|
852
875
|
};
|
|
853
876
|
|
|
854
|
-
dapp_service::
|
|
877
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
878
|
+
dapp_service::accept_invitation_in_scene_permit(permit, owner);
|
|
855
879
|
dubhe_events::emit_scene_permit_accept(
|
|
856
880
|
dapp_key_str,
|
|
857
881
|
*dapp_service::scene_permit_type(permit),
|
|
858
882
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
859
|
-
|
|
883
|
+
owner,
|
|
860
884
|
);
|
|
861
885
|
}
|
|
862
886
|
|
|
863
|
-
/// Helper: add the caller as a confirmed participant in a
|
|
887
|
+
/// Helper: add the caller's canonical owner as a confirmed participant in a
|
|
888
|
+
/// ScenePermit. The transaction may be signed by the canonical owner or its
|
|
889
|
+
/// active session key (is_write_authorized) — the registered identity is
|
|
890
|
+
/// always the canonical owner address.
|
|
864
891
|
/// The permit must still be active — joining an expired permit is meaningless and
|
|
865
892
|
/// wastes gas on a DF write that can never be used for reactive writes.
|
|
866
893
|
public fun join_scene_permit<DappKey: copy + drop, PermType>(
|
|
867
|
-
_auth:
|
|
868
|
-
permit:
|
|
869
|
-
|
|
894
|
+
_auth: DappKey,
|
|
895
|
+
permit: &mut ScenePermit<PermType>,
|
|
896
|
+
user_storage: &UserStorage,
|
|
897
|
+
ctx: &TxContext,
|
|
870
898
|
) {
|
|
871
899
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
872
900
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
873
|
-
error::
|
|
874
|
-
let
|
|
875
|
-
dapp_service::
|
|
901
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
902
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
903
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
904
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
905
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
906
|
+
let was_participant = dapp_service::is_participant_in_scene_permit(permit, owner);
|
|
907
|
+
dapp_service::add_participant_in_scene_permit(permit, owner);
|
|
876
908
|
if (!was_participant) {
|
|
877
909
|
dubhe_events::emit_scene_permit_join(
|
|
878
910
|
dapp_key_str,
|
|
879
911
|
*dapp_service::scene_permit_type(permit),
|
|
880
912
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
881
|
-
|
|
913
|
+
owner,
|
|
882
914
|
);
|
|
883
915
|
};
|
|
884
916
|
}
|
|
885
917
|
|
|
886
|
-
/// Helper: remove the caller from participants in a
|
|
918
|
+
/// Helper: remove the caller's canonical owner from participants in a
|
|
919
|
+
/// SceneStorage scene. The transaction may be signed by the canonical owner or
|
|
920
|
+
/// its active session key (is_write_authorized).
|
|
887
921
|
public fun leave_scene_permit<DappKey: copy + drop, PermType>(
|
|
888
|
-
_auth:
|
|
889
|
-
permit:
|
|
890
|
-
|
|
922
|
+
_auth: DappKey,
|
|
923
|
+
permit: &mut ScenePermit<PermType>,
|
|
924
|
+
user_storage: &UserStorage,
|
|
925
|
+
ctx: &TxContext,
|
|
891
926
|
) {
|
|
892
927
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
893
928
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
894
|
-
|
|
895
|
-
dapp_service::
|
|
929
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
930
|
+
error::no_permission(dapp_service::is_write_authorized(
|
|
931
|
+
user_storage, ctx.sender(), ctx.epoch_timestamp_ms()
|
|
932
|
+
));
|
|
933
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
934
|
+
let was_participant = dapp_service::is_participant_in_scene_permit(permit, owner);
|
|
935
|
+
dapp_service::remove_participant_in_scene_permit(permit, owner);
|
|
896
936
|
if (was_participant) {
|
|
897
937
|
dubhe_events::emit_scene_permit_leave(
|
|
898
938
|
dapp_key_str,
|
|
899
939
|
*dapp_service::scene_permit_type(permit),
|
|
900
940
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
901
|
-
|
|
941
|
+
owner,
|
|
902
942
|
);
|
|
903
943
|
};
|
|
904
944
|
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/// Unit tests — Session keys acting in scenes
|
|
2
|
+
///
|
|
3
|
+
/// Sessions are delegated proxies: a transaction signed by an active session
|
|
4
|
+
/// key must be able to perform in-scene actions on behalf of its canonical
|
|
5
|
+
/// owner, while the on-chain identity (permit participants, scene events)
|
|
6
|
+
/// always resolves to the canonical owner address.
|
|
7
|
+
///
|
|
8
|
+
/// Covers:
|
|
9
|
+
/// reactive writes:
|
|
10
|
+
/// session key may initiate set_record_reactive / set_field_reactive
|
|
11
|
+
/// expired session is rejected
|
|
12
|
+
/// stranger (neither owner nor session) is rejected
|
|
13
|
+
/// permit participation:
|
|
14
|
+
/// join_scene_permit registers the canonical owner, not the session address
|
|
15
|
+
/// accept_scene_permit_invitation moves the canonical owner to participants
|
|
16
|
+
/// leave_scene_permit removes the canonical owner
|
|
17
|
+
/// unauthorized sender cannot join on someone else's behalf
|
|
18
|
+
/// scene field writes:
|
|
19
|
+
/// session key may set_scene_field / remove_scene_field
|
|
20
|
+
/// non-participant canonical owner is rejected
|
|
21
|
+
#[test_only]
|
|
22
|
+
module dubhe::scene_session_test;
|
|
23
|
+
|
|
24
|
+
use dubhe::dapp_service::{Self, UserStorage, ScenePermit};
|
|
25
|
+
use dubhe::dapp_system;
|
|
26
|
+
use sui::bcs::to_bytes;
|
|
27
|
+
use sui::tx_context;
|
|
28
|
+
|
|
29
|
+
public struct SceneSessionKey has copy, drop {}
|
|
30
|
+
|
|
31
|
+
const OWNER: address = @0xA1;
|
|
32
|
+
const SESSION: address = @0xA2;
|
|
33
|
+
const PEER: address = @0xB1;
|
|
34
|
+
const OTHER: address = @0xC1;
|
|
35
|
+
|
|
36
|
+
const NOW: u64 = 1_000;
|
|
37
|
+
const SESSION_EXPIRES: u64 = 10_000;
|
|
38
|
+
|
|
39
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
fun make_us(owner: address, ctx: &mut TxContext): UserStorage {
|
|
42
|
+
dapp_service::create_user_storage_for_testing<SceneSessionKey>(owner, ctx)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// UserStorage owned by `owner` with an active session key bound to SESSION.
|
|
46
|
+
fun make_us_with_session(owner: address, ctx: &mut TxContext): UserStorage {
|
|
47
|
+
let mut us = make_us(owner, ctx);
|
|
48
|
+
dapp_service::set_session_key_for_testing(&mut us, SESSION, SESSION_EXPIRES);
|
|
49
|
+
us
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun make_permit(
|
|
53
|
+
participants: vector<address>,
|
|
54
|
+
ctx: &mut TxContext,
|
|
55
|
+
): ScenePermit<SceneSessionKey> {
|
|
56
|
+
dapp_service::create_scene_permit_for_testing<SceneSessionKey, SceneSessionKey>(
|
|
57
|
+
participants, std::option::none(), std::option::none(), ctx,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Transaction context whose sender is `sender` at epoch time NOW.
|
|
62
|
+
fun ctx_for(sender: address): TxContext {
|
|
63
|
+
tx_context::new_from_hint(sender, 0, 0, NOW, 0)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun key_for(name: vector<u8>): vector<vector<u8>> { vector[name] }
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// Reactive writes initiated by a session key
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
#[test]
|
|
73
|
+
fun test_session_can_initiate_reactive_write() {
|
|
74
|
+
let mut ctx = ctx_for(SESSION);
|
|
75
|
+
|
|
76
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
77
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
78
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
79
|
+
|
|
80
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
81
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
82
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
83
|
+
);
|
|
84
|
+
dapp_system::set_field_reactive<SceneSessionKey, SceneSessionKey>(
|
|
85
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
86
|
+
key_for(b"hp"), b"v", to_bytes(&80u64), &mut ctx,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
assert!(dapp_service::has_user_record<SceneSessionKey>(&target, key_for(b"hp")), 0);
|
|
90
|
+
// Fees are charged to the initiator's storage, not the target's.
|
|
91
|
+
assert!(dapp_service::write_count(&from) == 2, 1);
|
|
92
|
+
assert!(dapp_service::write_count(&target) == 0, 2);
|
|
93
|
+
|
|
94
|
+
dapp_service::destroy_user_storage(from);
|
|
95
|
+
dapp_service::destroy_user_storage(target);
|
|
96
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#[test]
|
|
100
|
+
#[expected_failure]
|
|
101
|
+
fun test_expired_session_reactive_aborts() {
|
|
102
|
+
// Session expires exactly at SESSION_EXPIRES — a transaction at that time is rejected.
|
|
103
|
+
let mut ctx = tx_context::new_from_hint(SESSION, 0, 0, SESSION_EXPIRES, 0);
|
|
104
|
+
|
|
105
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
106
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
107
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
108
|
+
|
|
109
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
110
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
111
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
dapp_service::destroy_user_storage(from);
|
|
115
|
+
dapp_service::destroy_user_storage(target);
|
|
116
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[test]
|
|
120
|
+
#[expected_failure]
|
|
121
|
+
fun test_stranger_reactive_aborts() {
|
|
122
|
+
let mut ctx = ctx_for(OTHER);
|
|
123
|
+
|
|
124
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
125
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
126
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
127
|
+
|
|
128
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
129
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
130
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
dapp_service::destroy_user_storage(from);
|
|
134
|
+
dapp_service::destroy_user_storage(target);
|
|
135
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
139
|
+
// Permit participation through a session key registers the canonical owner
|
|
140
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
141
|
+
|
|
142
|
+
#[test]
|
|
143
|
+
fun test_session_join_registers_canonical_owner() {
|
|
144
|
+
let mut ctx = ctx_for(SESSION);
|
|
145
|
+
|
|
146
|
+
let mut permit = make_permit(vector[], &mut ctx);
|
|
147
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
148
|
+
|
|
149
|
+
dapp_system::join_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
150
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
assert!(dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
154
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, SESSION), 1);
|
|
155
|
+
|
|
156
|
+
dapp_service::destroy_user_storage(us);
|
|
157
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#[test]
|
|
161
|
+
fun test_session_accept_invitation_registers_canonical_owner() {
|
|
162
|
+
let mut ctx = ctx_for(SESSION);
|
|
163
|
+
|
|
164
|
+
let mut permit = dapp_service::create_scene_permit_with_invitations_for_testing<
|
|
165
|
+
SceneSessionKey, SceneSessionKey
|
|
166
|
+
>(vector[OWNER], std::option::none(), std::option::none(), &mut ctx);
|
|
167
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
168
|
+
|
|
169
|
+
dapp_system::accept_scene_permit_invitation<SceneSessionKey, SceneSessionKey>(
|
|
170
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
assert!(dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
174
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, SESSION), 1);
|
|
175
|
+
|
|
176
|
+
dapp_service::destroy_user_storage(us);
|
|
177
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[test]
|
|
181
|
+
fun test_session_leave_removes_canonical_owner() {
|
|
182
|
+
let mut ctx = ctx_for(SESSION);
|
|
183
|
+
|
|
184
|
+
let mut permit = make_permit(vector[OWNER], &mut ctx);
|
|
185
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
186
|
+
|
|
187
|
+
dapp_system::leave_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
188
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
192
|
+
|
|
193
|
+
dapp_service::destroy_user_storage(us);
|
|
194
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
#[expected_failure]
|
|
199
|
+
fun test_join_unauthorized_sender_aborts() {
|
|
200
|
+
// OTHER is neither the canonical owner nor the session key of `us`.
|
|
201
|
+
let mut ctx = ctx_for(OTHER);
|
|
202
|
+
|
|
203
|
+
let mut permit = make_permit(vector[], &mut ctx);
|
|
204
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
205
|
+
|
|
206
|
+
dapp_system::join_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
207
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
dapp_service::destroy_user_storage(us);
|
|
211
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
215
|
+
// Scene field writes through a session key
|
|
216
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
217
|
+
|
|
218
|
+
#[test]
|
|
219
|
+
fun test_session_set_and_remove_scene_field() {
|
|
220
|
+
let mut ctx = ctx_for(SESSION);
|
|
221
|
+
|
|
222
|
+
let ds = dapp_service::create_dapp_storage_for_testing<SceneSessionKey>(&mut ctx);
|
|
223
|
+
let permit = make_permit(vector[OWNER], &mut ctx);
|
|
224
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
225
|
+
|
|
226
|
+
let mut scene = dapp_system::new_typed_scene_with_permit<
|
|
227
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey
|
|
228
|
+
>(SceneSessionKey {}, &ds, &permit, b"battle", &mut ctx);
|
|
229
|
+
|
|
230
|
+
dapp_system::set_scene_field<SceneSessionKey, SceneSessionKey, SceneSessionKey, u64>(
|
|
231
|
+
SceneSessionKey {}, &permit, &mut scene, &us, b"hp", 100u64, &ctx,
|
|
232
|
+
);
|
|
233
|
+
assert!(dapp_system::get_scene_field<SceneSessionKey, u64>(&scene, b"hp") == 100, 0);
|
|
234
|
+
|
|
235
|
+
let removed: u64 = dapp_system::remove_scene_field<
|
|
236
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey, u64
|
|
237
|
+
>(SceneSessionKey {}, &permit, &mut scene, &us, b"hp", &ctx);
|
|
238
|
+
assert!(removed == 100, 1);
|
|
239
|
+
|
|
240
|
+
dapp_system::destroy_typed_scene<SceneSessionKey, SceneSessionKey>(SceneSessionKey {}, scene);
|
|
241
|
+
dapp_service::destroy_user_storage(us);
|
|
242
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
243
|
+
dapp_system::destroy_dapp_storage(ds);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#[test]
|
|
247
|
+
#[expected_failure]
|
|
248
|
+
fun test_scene_field_non_participant_aborts() {
|
|
249
|
+
// `us` belongs to OTHER who never joined the permit — write must abort
|
|
250
|
+
// even though OTHER signed the transaction themselves.
|
|
251
|
+
let mut ctx = ctx_for(OTHER);
|
|
252
|
+
|
|
253
|
+
let ds = dapp_service::create_dapp_storage_for_testing<SceneSessionKey>(&mut ctx);
|
|
254
|
+
let permit = make_permit(vector[OWNER], &mut ctx);
|
|
255
|
+
let us = make_us(OTHER, &mut ctx);
|
|
256
|
+
|
|
257
|
+
let mut scene = dapp_system::new_typed_scene_with_permit<
|
|
258
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey
|
|
259
|
+
>(SceneSessionKey {}, &ds, &permit, b"battle", &mut ctx);
|
|
260
|
+
|
|
261
|
+
dapp_system::set_scene_field<SceneSessionKey, SceneSessionKey, SceneSessionKey, u64>(
|
|
262
|
+
SceneSessionKey {}, &permit, &mut scene, &us, b"hp", 100u64, &ctx,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
dapp_system::destroy_typed_scene<SceneSessionKey, SceneSessionKey>(SceneSessionKey {}, scene);
|
|
266
|
+
dapp_service::destroy_user_storage(us);
|
|
267
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
268
|
+
dapp_system::destroy_dapp_storage(ds);
|
|
269
|
+
}
|