From c6a5447af1fffefe00f07dbcb04cbd66a2b0a51d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Apr 2026 06:08:53 +0000 Subject: [PATCH 1/2] Phase 0: dual-sig security fix + extraData/salt width narrowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per OPT_PLAN.md §9 Phase 0, in preparation for the batched-execute work: 1. Security fix to `SignedCommitManager.executeWithDualSignedMoves`: - Add `bytes calldata committerSignature` parameter before `revealerSignature`. - Recover committer signature against `SignedCommit{committerMoveHash, battleKey, turnId}`; require equality with the committer. - Drop the fragile `msg.sender == committer` check (and `error CallerNotCommitter`). The function is now relayer-friendly: anyone with both valid signatures + the committer preimage can submit. - Closes the "unilateral revealer" attack: previously a malicious revealer could sign `DualSignedReveal{committerMoveHash: keccak(P*), …}` for any preimage `P*` and submit (when the msg.sender check evolved away, batching, etc.). Now the explicit committer signature binds the committer cryptographically. 2. Width narrowing (clean break, no shims) — touched together because `DualSignedReveal`'s EIP-712 typehash includes both fields: - `extraData`: 240 → 16 bits across `IMoveSet`, all 50+ mon moves/abilities, `Engine`/`IEngine` (`setMove`, `executeWithMoves`, `executeWithSingleMove`, `validatePlayerMoveForBattle`, `MoveDecision.extraData`, `RevealedMove.extraData`), `IValidator`/`DefaultValidator`, `ValidatorLogic`, the commit managers, the CPU stack, and `SleepStatus`. - `salt`: 256 → 104 bits across the same surface, plus the engine's `_turnP{0,1}Salt` transients, `BattleConfig{,View}.{p0,p1}Salt`, `RevealedMove.salt`, and the `MonMove` event signature. - `SignedCommitLib.DualSignedReveal.revealerSalt` and the matching EIP-712 typehash updated to `uint104` / `uint16`. - Random-oracle interface kept as `bytes32` (it's an arbitrary-input hash surface, not a security boundary); `Engine` casts at the call site. - Test mocks repacked into 16 bits with explicit layouts: - `_packStatBoost` / `StatBoostsMove`: `[boost:8 | stat:4 | mon:3 | player:1]` - `_packForceSwitch` / `ForceSwitchMove`: `[mon:15 | player:1]` - `EditEffectAttack`: `[effectIdx:10 | monIdx:4 | targetIdx:2]` - `MockKVWriterMove`: `[value:6 | key:10]` - `MockEffectRemover`: switched from passing the effect address (no longer fits) to passing the slot index from `getEffects`. 3. New TDD regression tests in `SignedCommitManager.t.sol`: - `test_executeWithDualSigned_thirdPartyRelay_succeeds` — drops msg.sender check; arbitrary relayer with both valid sigs can submit. - `test_revert_executeWithDualSigned_unilateralRevealerAttack` — revealer alone (forging committer sig with their own key) reverts on the committer-sig recovery. - `test_revert_executeWithDualSigned_wrongCommitterSigner` — committer sig recovers to a non-committer address. - `test_revert_executeWithDualSigned_committerSigForWrongHash` — committer signs a different `moveHash` than the submitted preimage → engine recomputes from preimage, mismatch reverts. 4. Imported the OPT_PLAN.md and BatchInstrumentationTest.sol scaffolding from `feat/next` so subsequent phases land on the same plan. Two `BetterCPU` strategy tests (`test_betterCPUSelectsHighestDamageMove`, `test_defensiveSwitch_materialityNotMet`) needed a CPU-mon speed bump to deterministically break the speed tie: with old 256-bit salts the salt-derived RNG happened to give CPU priority, with 104-bit salts it doesn't, so the original tests' assumption was implicit. The fix is to make the priority deterministic instead of leaning on RNG. Gas snapshots refreshed across the affected suites — dual-signed flow gains the second `ecrecover`, partly offset by narrower calldata. `forge build` clean. `forge test`: 352/352 passing. https://claude.ai/code/session_01Lc98i85bMi3SWTBnaNiDuZ --- OPT_PLAN.md | 386 ++++++++++++++++++++ snapshots/BetterCPUInlineGasTest.json | 12 +- snapshots/EngineGasTest.json | 36 +- snapshots/EngineOptimizationTest.json | 4 +- snapshots/FullyOptimizedInlineGasTest.json | 12 +- snapshots/InlineEngineGasTest.json | 28 +- snapshots/MatchmakerTest.json | 6 +- snapshots/StandardAttackPvPGasTest.json | 10 +- src/DefaultValidator.sol | 6 +- src/Engine.sol | 42 +-- src/IEngine.sol | 14 +- src/IValidator.sol | 4 +- src/Structs.sol | 16 +- src/commit-manager/DefaultCommitManager.sol | 2 +- src/commit-manager/ICommitManager.sol | 2 +- src/commit-manager/SignedCommitLib.sol | 8 +- src/commit-manager/SignedCommitManager.sol | 84 +++-- src/cpu/BetterCPU.sol | 10 +- src/cpu/CPU.sol | 30 +- src/cpu/CPUMoveManager.sol | 6 +- src/cpu/ICPU.sol | 4 +- src/cpu/OkayCPU.sol | 6 +- src/cpu/PlayerCPU.sol | 8 +- src/cpu/RandomCPU.sol | 4 +- src/effects/status/SleepStatus.sol | 2 +- src/lib/ValidatorLogic.sol | 2 +- src/mons/aurox/BullRush.sol | 2 +- src/mons/aurox/GildedRecovery.sol | 6 +- src/mons/aurox/IronWall.sol | 4 +- src/mons/aurox/VolatilePunch.sol | 2 +- src/mons/ekineki/BubbleBop.sol | 2 +- src/mons/ekineki/NineNineNine.sol | 4 +- src/mons/ekineki/Overflow.sol | 2 +- src/mons/ekineki/SneakAttack.sol | 4 +- src/mons/embursa/HeatBeacon.sol | 4 +- src/mons/embursa/HoneyBribe.sol | 4 +- src/mons/embursa/Q5.sol | 4 +- src/mons/embursa/SetAblaze.sol | 2 +- src/mons/ghouliath/EternalGrudge.sol | 4 +- src/mons/ghouliath/WitherAway.sol | 2 +- src/mons/gorillax/RockPull.sol | 4 +- src/mons/iblivion/Brightback.sol | 4 +- src/mons/iblivion/Loop.sol | 4 +- src/mons/iblivion/Renormalize.sol | 4 +- src/mons/iblivion/UnboundedStrike.sol | 4 +- src/mons/inutia/ChainExpansion.sol | 4 +- src/mons/inutia/HitAndDip.sol | 4 +- src/mons/inutia/Initialize.sol | 4 +- src/mons/malalien/TripleThink.sol | 4 +- src/mons/pengym/Deadlift.sol | 4 +- src/mons/pengym/DeepFreeze.sol | 4 +- src/mons/pengym/PistolSquat.sol | 2 +- src/mons/sofabbi/Gachachacha.sol | 4 +- src/mons/sofabbi/GuestFeature.sol | 4 +- src/mons/sofabbi/SnackBreak.sol | 4 +- src/mons/volthare/DualShock.sol | 2 +- src/mons/volthare/MegaStarBlast.sol | 4 +- src/mons/volthare/RoundTrip.sol | 4 +- src/mons/xmon/ContagiousSlumber.sol | 4 +- src/mons/xmon/NightTerrors.sol | 4 +- src/mons/xmon/Somniphobia.sol | 4 +- src/mons/xmon/VitalSiphon.sol | 2 +- src/moves/IMoveSet.sol | 4 +- src/moves/StandardAttack.sol | 4 +- test/BatchInstrumentationTest.sol | 378 +++++++++++++++++++ test/BattleHistoryTest.sol | 10 +- test/BetterCPUInlineGasTest.sol | 16 +- test/BetterCPUTest.sol | 169 +++++---- test/CPUTest.sol | 46 +-- test/DefaultCommitManagerTest.sol | 14 +- test/EngineGasTest.sol | 69 ++-- test/EngineGlobalKVTest.sol | 25 +- test/EngineOptimizationTest.sol | 44 +-- test/EngineTest.sol | 180 ++++----- test/GachaTest.sol | 8 +- test/InlineAbilityParityTest.sol | 18 +- test/InlineEngineGasTest.sol | 174 +++++---- test/InlineMoveParityTest.sol | 10 +- test/InlineValidationTest.sol | 40 +- test/MatchmakerTest.sol | 2 +- test/SignedCommitManager.t.sol | 386 +++++++++++++------- test/SignedCommitManagerGasBenchmark.t.sol | 58 +-- test/StandardAttackPvPGasTest.sol | 45 ++- test/abstract/BattleHelper.sol | 10 +- test/effects/EffectTest.sol | 28 +- test/effects/StatBoosts.t.sol | 25 +- test/mocks/CustomAttack.sol | 4 +- test/mocks/EditEffectAttack.sol | 13 +- test/mocks/EffectAttack.sol | 4 +- test/mocks/ForceSwitchMove.sol | 10 +- test/mocks/GlobalEffectAttack.sol | 4 +- test/mocks/InvalidMove.sol | 4 +- test/mocks/MockEffectRemover.sol | 24 +- test/mocks/MockKVWriterMove.sol | 15 +- test/mocks/ReduceSpAtkMove.sol | 4 +- test/mocks/SelfSwitchAndDamageMove.sol | 4 +- test/mocks/SkipTurnMove.sol | 4 +- test/mocks/StatBoostsMove.sol | 14 +- test/mocks/TestMoveFactory.sol | 4 +- test/mons/AuroxTest.sol | 28 +- test/mons/EkinekiTest.sol | 42 +-- test/mons/EmbursaTest.sol | 24 +- test/mons/GhouliathTest.sol | 22 +- test/mons/GorillaxTest.sol | 8 +- test/mons/IblivionTest.sol | 44 ++- test/mons/InutiaTest.sol | 24 +- test/mons/MalalienTest.sol | 10 +- test/mons/PengymTest.sol | 48 +-- test/mons/SofabbiTest.sol | 20 +- test/mons/VolthareTest.sol | 16 +- test/mons/XmonTest.sol | 20 +- test/moves/StandardAttackRngTest.sol | 2 +- 112 files changed, 2036 insertions(+), 1015 deletions(-) create mode 100644 OPT_PLAN.md create mode 100644 test/BatchInstrumentationTest.sol diff --git a/OPT_PLAN.md b/OPT_PLAN.md new file mode 100644 index 00000000..ac3d8c84 --- /dev/null +++ b/OPT_PLAN.md @@ -0,0 +1,386 @@ +# OPT_PLAN — Batched Execute Gas Optimization + +## 1. Goal + +Amortize per-turn cold-storage access in `Engine.execute()` by: +1. Submitting each turn's signed moves on-chain immediately to a per-turn buffer (no execute). +2. Executing **all currently buffered turns** in one tx with engine state held in **transient shadow storage**, flushed to persistent storage once at the end. + +Secondary goal: route `Engine` state access through helpers so the single-turn path can also use the shadow layer. + +--- + +## 2. Mechanism + +### 2.1 Per-turn submission (PvP) + +`SignedCommitManager.submitTurnMoves(battleKey, TurnSubmission entry)`: +- Uniform shape every turn: **two EIP-712 signatures** (committer + revealer), committer preimage in calldata. Roles derived from `turnId % 2` (matching `getCommitAuthForDualSigned`). +- Switch turns use the same shape. The non-acting player signs a `NO_OP` (move 126); engine ignores their half at batch time using the live `playerSwitchForTurnFlag`. +- Manager hashes committer preimage, verifies committer sig over `SignedCommit{committerMoveHash, …}` and revealer sig over `DualSignedReveal{committerMoveHash, …}`, writes to `moveBuffer[storageKey][turnId]`. **No execute runs.** +- Updates `lastSubmitTimestamp` for timeout tracking. + +**Why two sigs.** Without a committer sig, a malicious revealer could pick any preimage `P*`, sign `DualSignedReveal{committerMoveHash: keccak(P*), …}`, and submit unilaterally — the contract would play `P*` as the committer's move with no committer involvement. Today's `executeWithDualSignedMoves` blocks this only via `msg.sender == committer`, which is fragile and not relayer-friendly. Phase 0 (§9) lifts the same fix into the existing function before any batching ships, so both paths share one security model. + +### 2.2 Per-batch execute + +`Engine.executeBatch(battleKey)`: +- Anyone can call (sigs were checked at submission). +- Reads every currently buffered entry `[startTurn, startTurn + numTurnsBuffered)`, runs each in sequence inside transient shadow storage, flushes once at end. +- The **transient mirror** of `turnId` advances inside the loop. Persistent `BattleData.turnId` advances only during the final flush. +- Batch execution always consumes the full pending buffer. There is no partial-batch mode in v1. +- Processed buffer slots are not cleared — the unbounded mapping leaves them for on-chain replay. Slot reuse across battles comes from `MappingAllocator`. + +### 2.3 Fallback / stalls + +Fully separate write paths. Legacy `DefaultCommitManager.commitMove`/`revealMove` writes `config.p0Move` etc. and triggers `execute()` immediately; the batched path never reads that storage. A battle can alternate between modes turn-by-turn. Timeout via `Engine.end()` covers full stalls. + +--- + +## 3. Buffer layout + +One 256-bit slot per turn: + +```solidity +// [ p0MoveIndex (8) | p0ExtraData (16) | p0Salt (104) | p1MoveIndex (8) | p1ExtraData (16) | p1Salt (104) ] +struct PackedTurnEntry { + uint8 p0MoveIndex; + uint16 p0ExtraData; + uint104 p0Salt; + uint8 p1MoveIndex; + uint16 p1ExtraData; + uint104 p1Salt; +} + +mapping(bytes32 storageKey => mapping(uint64 turnId => PackedTurnEntry)) moveBuffer; +``` + +Steady-state cost per turn: 1 SSTORE (5k, nonzero→nonzero from prior battle's slot reuse) + 1 SLOAD inside batch (2.1k) = ~7.1k. + +Buffer validity is tracked by two packed `uint8` counters: +- `numTurnsBuffered`: number of currently pending buffered turns. +- `numTurnsExecuted`: cumulative number of buffered turns consumed for the current battle/storage key. + +Submit rule: +- If `numTurnsBuffered == 0`, the manager first syncs `numTurnsExecuted` to the engine's current `BattleData.turnId`. This keeps the batched buffer compatible with legacy single-turn execution when the battle alternates modes. +- A new entry must have `entry.turnId == numTurnsExecuted + numTurnsBuffered`. +- After storing the entry, increment `numTurnsBuffered`. + +Execute rule: +- `executeBatch` requires `numTurnsBuffered > 0`. +- It attempts the full pending range of `numTurnsBuffered` turns, starting at `numTurnsExecuted`. +- At flush, persistent `BattleData.turnId` becomes the shadowed turn id, `numTurnsExecuted += executedTurns`, and `numTurnsBuffered = 0`. + +This means stale slots from a prior battle or earlier batch cannot be treated as valid pending moves: only the contiguous range described by `(numTurnsExecuted, numTurnsBuffered)` is live. + +**Width changes (clean break):** +- `extraData`: 240 → 16 bits. Audit confirmed all production consumers read ≤8 bits. Narrow `IMoveSet.move()`'s `extraData` param to `uint16`; repack test helpers (`_packStatBoost`, `StatBoostsMove` mock). +- `Salt`: 256 → 104 bits. 2^104 brute-force resistance is sufficient for the seconds-to-minutes commit-reveal window. + +--- + +## 4. API + +### 4.1 Submission + +```solidity +struct TurnSubmission { + uint64 turnId; + // Committer preimage: + uint8 committerMoveIndex; + uint16 committerExtraData; + uint104 committerSalt; + // Revealer reveal: + uint8 revealerMoveIndex; + uint16 revealerExtraData; + uint104 revealerSalt; + // Sigs: + bytes committerSig; // EIP-712 over SignedCommit{committerMoveHash, battleKey, turnId} + bytes revealerSig; // EIP-712 over DualSignedReveal +} + +// Existing SignedCommitLib struct, reused unchanged. +struct SignedCommit { + bytes32 moveHash; + bytes32 battleKey; + uint64 turnId; +} + +struct DualSignedReveal { + bytes32 battleKey; + uint64 turnId; + bytes32 committerMoveHash; // keccak(committerMoveIndex, committerSalt, committerExtraData) + uint8 revealerMoveIndex; + uint16 revealerExtraData; + uint104 revealerSalt; +} + +function submitTurnMoves(bytes32 battleKey, TurnSubmission calldata entry) external; +``` + +Manager flow: +1. Battle is in dual-signed mode and not over. +2. `entry.turnId` equals next append position. +3. Derive `(committer, revealer)` from `turnId % 2`. +4. `committerMoveHash = keccak(committerMoveIndex, committerSalt, committerExtraData)`. +5. Recover `committerSig` over `SignedCommit{committerMoveHash, battleKey, turnId}`; require equality with `committer`. +6. Recover `revealerSig` over `DualSignedReveal{committerMoveHash, …}`; require equality with `revealer`. +7. Map fields to `(p0, p1)` by parity; SSTORE `PackedTurnEntry`. + +### 4.2 Batch execute + +```solidity +function executeBatch(bytes32 battleKey) external; +``` + +1. Read `startTurn = numTurnsExecuted`; require `numTurnsBuffered > 0`. +2. Hydrate shadow. +3. For each pending buffered turn: read buffer slot, populate per-turn move/salt transient, run `_executeOneTurn()`, break on game-over. +4. Flush shadow → storage. +5. Set `numTurnsBuffered = 0` and increment `numTurnsExecuted` by the number of turns actually executed. + +--- + +## 5. Transient shadow storage + +### 5.1 Shadowed state + +| Storage | Shadow form | +|---|---| +| `MonState` (per mon) | Per-`(playerIndex, monIndex)` mirror, lazy-loaded. Dirty bit per slot. | +| `koBitmaps` (16 bits in `BattleConfig` slot 2) | `uint16` mirror, loaded flag. | +| `winnerIndex` / `prevPlayerSwitchForTurnFlag` / `playerSwitchForTurnFlag` / `activeMonIndex` / `turnId` / `lastExecuteTimestamp` | Single packed `uint256` mirror. | +| Effect list slots (`globalEffects[i]`, `pXEffects[i]`) | Sparse transient map keyed by list + slot index, mirrors the full `EffectInstance` (`effect`, `stepsBitmap`, `data`). | +| `packedP0EffectsCount` / `packedP1EffectsCount` / `globalEffectsLength` | Three small mirrors, flushed with effect-list shadow. | +| `globalKV[storageKey][key]` | Per-`key` mirror, lazy-loaded. | +| `BattleConfig.p0Move` / `p1Move` / salts | Re-populated per sub-turn from buffer slot. | + +Hydrate strategy: +- **Eager**: `BattleData` slot 1 + `BattleConfig` slot 2 (always touched). +- **Lazy**: `MonState`, effect slots/counts, `globalKV` (sparse — pay only for slots touched). + +Loaded-flag strategy: +- **Bitmap** for fixed-shape slots (MonState, effects, slot-2 packed fields). +- **Per-key transient hash-set** for `globalKV` (dynamic keys). + +### 5.2 Helper boundary + +Mirrored helpers in `Engine.sol`: + +```solidity +function _shadowReadMonState(BattleConfig storage cfg, uint256 playerIndex, uint256 monIndex) internal returns (MonState memory); +function _shadowWriteMonState(uint256 playerIndex, uint256 monIndex, MonState memory state) internal; +function _shadowReadKV(bytes32 storageKey, uint64 key) internal returns (uint192); +function _shadowWriteKV(bytes32 storageKey, uint64 key, uint192 value) internal; +function _shadowReadEffectSlot(uint256 effectList, uint256 monIndex, uint256 slotIndex) internal returns (EffectInstance memory); +function _shadowWriteEffectSlot(uint256 effectList, uint256 monIndex, uint256 slotIndex, EffectInstance memory eff) internal; +function _shadowReadEffectCount(uint256 effectList, uint256 monIndex) internal returns (uint256); +function _shadowWriteEffectCount(uint256 effectList, uint256 monIndex, uint256 count) internal; +``` + +When `_shadowActive == false`, helpers SLOAD/SSTORE storage directly. When `true`, they read/write the transient mirror with lazy-load and dirty-bit bookkeeping. + +External `IEngine` writers (`updateMonState`, `dealDamage`, `addEffect`, `removeEffect`, `editEffect`, `setGlobalKV`, `switchActiveMon`, `dispatchStandardAttack`, `setMove`) and external readers (`getMonStateForBattle`, `getEffects`, `getGlobalKV`, etc.) all route through these helpers. The `battleKeyForWrite != bytes32(0)` gate stays. + +Effect-list shadowing must preserve these same-batch visibility rules: +- `addEffect` writes a full shadow `EffectInstance` and increments the shadow count, so later effect loops / `getEffects` calls in the same batch see the new effect. +- `editEffect` updates shadow `data`; later hooks see the edited value. +- `removeEffect` tombstones the shadow `effect` address and keeps the slot index stable; later loops skip it. +- `_handleEffects` loads counts and slots from shadow, not storage, and keeps the existing `effectsDirtyBitmap` pattern so effects added while iterating can extend the current loop when today’s logic would. +- `getEffects` builds its return arrays from shadow while `_shadowActive == true`, so external moves/effects that inspect active effects observe the live batch state. + +### 5.3 Batch loop + +``` +executeBatch(battleKey): + storageKey = _getStorageKey(battleKey) + storageKeyForWrite = storageKey + battleKeyForWrite = battleKey + _shadowActive = true + + _hydrateBattleData(battleKey) + _hydrateConfigSlot2(storageKey) + + startTurn = numTurnsExecuted + turnsToExecute = numTurnsBuffered + for t in [startTurn .. startTurn + turnsToExecute): + bufferEntry = _readMoveBufferSlot(storageKey, t) + _populateTurnMoveTransient(bufferEntry) + _executeOneTurn() + if winnerIndex != 2: break + _resetPerTurnTransients() + + _flushBattleData(battleKey) + _flushConfigSlot2(storageKey) + _flushDirtyMonStates(storageKey) + _flushDirtyEffectSlots(storageKey) + _flushDirtyGlobalKV(storageKey) + _flushBufferCounters(executedTurns) + + _shadowActive = false +``` + +Per sub-turn, `tempRNG = keccak(p0Salt, p1Salt)` (or single signed salt for switch turns). Engine hooks (`onRoundStart`, `onRoundEnd`) fire per sub-turn and read shadow state via the routed getters. + +--- + +## 6. Forced switches and game-over + +### 6.1 Forced switch (KO without game-over) + +Both players sign for every turn. The non-acting player signs `NO_OP`. At batch time, the engine reads the live `playerSwitchForTurnFlag` (cheap — in shadow state) and dispatches: +- `flag == 2`: process both halves. +- `flag == 0`: process p0 only, ignore p1's NO_OP. +- `flag == 1`: mirror. + +A player who maliciously signs a non-NO_OP on a turn they shouldn't act has bound themselves cryptographically, but the engine ignores the move. A player who refuses to sign stalls the batched flow; legacy single-turn paths remain as fallback. + +Submission validates only cheap invariants (battle exists, not over at last flush, append position, sig). It does **not** project `playerSwitchForTurnFlag`, since that would require replaying every unprocessed turn. + +### 6.2 Game-over mid-batch + +`_executeInternal` already breaks when `winnerIndex != 2`. Same check stops the batch loop. Because batch execution consumes the full pending buffer, any unexecuted buffered entries after game-over remain in storage for replay but are no longer live; `numTurnsBuffered` is set to zero at flush. + +### 6.3 Status-induced skip-turn + +`shouldSkipTurn` already auto-clears in `_handleMove`. No special batch handling. + +--- + +## 7. CPU mode (trusted-state batched) + +Same per-turn buffer + `executeBatch` as PvP. CPU manager packs `(Alice move, computed CPU move)` into the same `PackedTurnEntry` layout. **Zero engine changes.** + +### 7.1 Trusted state hint + +Alice supplies the projected post-prior-turn `CPUContext` in calldata. Not verified. Lying never benefits Alice — it makes the CPU's chosen move suboptimal against her, which she absorbs. This replaces the dozen-plus cold SLOADs `engine.getCPUContext(battleKey)` does today with a single calldata struct. + +### 7.2 No signature + +Alice calls directly from her wallet. Manager checks `msg.sender == alice` (same as today's `CPUMoveManager.selectMove`). The tx is the proof — no relay path needed for a single-human flow. + +### 7.3 Off-chain protocol + +Each turn, locally on Alice's client: +1. Hold current `CPUContext`-shaped state. Turn 0 = post-`startBattle` state; later turns = output of last local sim. +2. Pick Alice's move. +3. Run the transpiled engine locally to produce the post-turn state, used as next turn's hint. +4. Submit on-chain with the **current-turn** hint. + +### 7.4 Submission + +```solidity +function selectMoveWithStateHint( + bytes32 battleKey, + uint8 aliceMoveIndex, + uint16 aliceExtraData, + uint104 aliceSalt, + CPUContext calldata projectedState +) external; +``` + +1. Read/sync the next append `turnId` from `numTurnsExecuted + numTurnsBuffered` using the same buffer counter rules as PvP. +2. Require `msg.sender == alice`. +3. Route on `projectedState.playerSwitchForTurnFlag` (single-player vs two-player CPU branch). +4. `ICPU(cpuAddr).calculateMove(projectedState, aliceMoveIndex, aliceExtraData)` → `(cpuMove, cpuExtra)`. CPU reads from calldata only. +5. Derive CPU salt: `uint104(uint256(keccak256(abi.encode(block.timestamp, aliceSalt, turnId))))`. Emit `CPUTurnSalt(battleKey, turnId, timestamp)` so off-chain replay can reconstruct it. `turnId` in the hash prevents collision when Alice submits multiple CPU turns in the same block. +6. Pack into `PackedTurnEntry` and SSTORE into `moveBuffer[storageKey][turnId]`. + +`executeBatch` is shared with PvP — the engine doesn't know whether the buffer came from PvP or CPU submissions. + +### 7.5 Coexistence + +Battles select via the `moveManager` they're started with: +- `signedCommitManager` (extended) → PvP batched +- `cpuMoveManager` (extended) → CPU batched +- Today's unmodified managers → legacy single-turn paths + +Today's `CPUMoveManager.selectMove` stays callable for any battle that doesn't opt into batching. + +--- + +## 8. Migration + +Add new entry points alongside existing ones. No "batch mode" flag on a battle — `executeBatch` works on any battle that has buffered turns. + +Touched contracts: +- `Engine.sol`: `executeBatch` + shadow-transient layer + helper routing + flag-based per-turn dispatch. +- `IEngine.sol`: new function signatures. +- `SignedCommitManager.sol`: `submitTurnMoves` (sharing existing EIP-712 domain). +- `CPUMoveManager.sol`: `selectMoveWithStateHint`. +- `IMoveSet.sol`: narrow `extraData` to `uint16`. ~40 mon files take mechanical edits. + +Validator/legality is unchanged: signature recovery proves player intent (or `msg.sender == alice` for CPU); state-dependent illegality silently no-ops in `_handleMove`. Timeout reads `lastSubmitTimestamp` and `lastExecuteTimestamp` — whichever is more recent. + +--- + +## 9. Phased rollout + +**Phase 0 — Dual-sig security fix + width changes (preflight, ships first, independent of batching).** The existing `executeWithDualSignedMoves` relies on `msg.sender == committer` as the committer's binding. Without that check, a malicious revealer could sign `DualSignedReveal{committerMoveHash: keccak(P*), …}` for any preimage `P*` they choose and submit unilaterally — the contract would happily compute `committerMoveHash = keccak(P*)`, recover the revealer's sig, and play `P*` as the committer's move. The check is load-bearing today, but it's also fragile: any future evolution of the flow that drops or weakens it (relayers, batching, alt entry points) silently re-opens the hole. + +Fix: require an explicit committer signature over the existing `SignedCommit{moveHash, battleKey, turnId}` struct (already used by `commitWithSignature`). + +Bundled with the security fix in this same phase: the §3 width changes (`extraData` 240→16 bits, `salt` 256→104 bits). They share callers with the dual-signed flow, including the EIP-712 `DualSignedReveal` typehash — touching them together avoids two breaking-change waves through the same surface. + +- Modify `executeWithDualSignedMoves` to take an additional `bytes calldata committerSignature` parameter. +- Recover `committerSignature` over `SignedCommit{committerMoveHash, battleKey, turnId}`; require equality with `committer`. +- Drop the `msg.sender == committer` check (and remove `error CallerNotCommitter`); the function becomes relayer-friendly (anyone with both sigs + the preimage can submit). +- Narrow `IMoveSet.move()` / `isValidTarget()` `extraData` to `uint16`; narrow `RevealedMove`/`MoveDecision`/`BattleConfig.{p0,p1}Salt`/event `MonMove.salt` and all commit-manager / engine entry points (`setMove`, `executeWithMoves`, `executeWithSingleMove`, `revealMove`, `executeSinglePlayerMove`, `selectMove`) to `uint104 salt` and `uint16 extraData`. +- Update `SignedCommitLib.DualSignedReveal` (`uint104 revealerSalt`, `uint16 revealerExtraData`) and the matching EIP-712 typehash. +- Repack the `_packStatBoost` test helper + `StatBoostsMove` mock from 4×60 bits into 16 bits: `[boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1]`. +- Breaking signature change. Update all callers (tests, `BattleHelper`, anything off-chain that calls this function) in the same PR. No deployed callers in production yet. +- New tests: third-party relayer with both valid sigs succeeds; revealer alone (with garbage/missing committer sig) cannot submit a self-chosen committer preimage (regression for unilateral-revealer attack); wrong committer signer reverts; committer sig over a different moveHash reverts. + +This phase ships before any batching work. It hardens the existing flow on its own merits, narrows the wire formats once (uint104 salt + uint16 extraData are the shapes Phase 2's `PackedTurnEntry` needs anyway), and unifies the security model so the batched path in Phase 2 inherits the same shape (§4.1) without surprises. + +**Status (Phase 0 complete):** +- ✅ Width sweep across `src/`: `IMoveSet`, `IEngine`, `Engine`, `IValidator`, `DefaultValidator`, `ValidatorLogic`, `ICommitManager`, `DefaultCommitManager`, `SignedCommitManager`, `SignedCommitLib` (incl. EIP-712 typehash), `CPUMoveManager`, `BetterCPU`/`OkayCPU`/`RandomCPU`/`PlayerCPU`/`CPU`, `SleepStatus`, all 50+ mon move/ability files, `Structs.sol` (`MoveDecision.extraData`, `RevealedMove.{salt,extraData}`, `BattleConfig{,View}.{p0,p1}Salt`), and `Engine`'s transient `_turnP{0,1}Salt` + `MonMove` event signature. +- ✅ Width sweep across `test/`: `BattleHelper`, all top-level test files, all per-mon tests, mocks (`StatBoostsMove`, `ForceSwitchMove`, `EditEffectAttack`, `MockKVWriterMove`, `MockEffectRemover`), and `_packStatBoost`/`_packForceSwitch` test helpers repacked into 16 bits. +- ✅ Committer signature parameter (`bytes calldata committerSignature` before `revealerSignature`) added to `executeWithDualSignedMoves`. `msg.sender == committer` check and `error CallerNotCommitter` removed. EIP-712 recovery against `SignedCommit{committerMoveHash, battleKey, turnId}` enforces committer binding. +- ✅ All test helpers (`_completeTurnFast`, `_executeDualSigned`, `_fastTurn` across `SignedCommitManager.t.sol`, `BatchInstrumentationTest`, `InlineEngineGasTest`, `StandardAttackPvPGasTest`, `SignedCommitManagerGasBenchmark`) produce a committer signature in addition to the revealer signature. +- ✅ New TDD security tests added in `SignedCommitManager.t.sol`: + - `test_executeWithDualSigned_thirdPartyRelay_succeeds` — drops `msg.sender` check; arbitrary relayer with both valid sigs can submit. + - `test_revert_executeWithDualSigned_unilateralRevealerAttack` — regression: revealer alone (forging committer sig with their own key) cannot inject a self-chosen committer preimage. + - `test_revert_executeWithDualSigned_wrongCommitterSigner` — committer-sig recovers to revealer's address → revert. + - `test_revert_executeWithDualSigned_committerSigForWrongHash` — committer signs a different `moveHash` than the submitted preimage → engine recomputes from preimage, mismatch → revert. +- ✅ Forge build clean (0 errors). +- ✅ Forge test green (352/352 passing). Two BetterCPU strategy tests (`test_betterCPUSelectsHighestDamageMove`, `test_defensiveSwitch_materialityNotMet`) needed CPU-mon speed bumps to break a salt-derived RNG tiebreaker — collateral effect of the salt narrowing. +- ✅ Gas snapshots refreshed for all impacted tests. Dual-signed flow shows expected ECDSA-recovery cost increase from the second `ecrecover`; calldata savings from narrower types partially offset. +- ⏭ Phase 0.5 (helper extraction, no behavior change) is the next planned phase per §9. + +**Phase 0.1 — Instrumentation refresh.** `test/BatchInstrumentationTest.sol` already wires `vm.startStateDiffRecording` for the clean damage-trade case. Add scenarios: effect-heavy turn (status DOT + StatBoosts active), forced-switch turn, multi-mon turn. Lock final batch-size guidance. + +**Phase 0.5 — Helper extraction (no behavior change).** Replace direct `MonState`/`globalKV`/effect-data SLOAD/SSTORE in `Engine.sol` with §5.2 helpers, with `_shadowActive` permanently `false`. Snapshot diff should be roughly flat. + +**Phase 1 — Single-turn shadow.** Implement transient mirrors + lazy-load/dirty-flag bookkeeping. Wire helpers to consult `_shadowActive`. Add `executeShadowed(bytes32 battleKey)` that does `execute()`'s work inside the shadow layer (hydrate → run one turn → flush). Existing test suite should pass against it. B=1 will be slightly *worse* than today's `execute()` due to bookkeeping overhead; expected. + +**Phase 2 — PvP per-turn submission + batch execute.** Extend `SignedCommitManager` with `submitTurnMoves`. Add per-turn move buffer mapping and `numTurnsBuffered` / `numTurnsExecuted` counters. Add `Engine.executeBatch` with flag-based dispatch (§6.1), requiring execution of all currently buffered turns. Equivalence tests + gas snapshots. + +**Phase 2.5 — CPU mode.** Extend `CPUMoveManager` with `selectMoveWithStateHint` (§7.4). Reuse Phase-2 buffer + `executeBatch`. Equivalence test: 24-turn CPU game via legacy `selectMove × 24` vs `selectMoveWithStateHint × 24 + executeBatch × 3` produces identical end state. + +**Phase 3 — Transpiler parity (deferred).** Local TS engine continues running single-turn `execute()` against hydrated state. Eventual batched parity desired but not v1. + +**Phase 4 — Optional cutover.** If `executeShadowed` (B=1) is gas-neutral or better, consider redirecting. Otherwise keep the legacy fast path. + +--- + +## 10. Test surface + +New `BattleHelper` helpers: +- `_submitTurnMoves(battleKey, turnId, p0Move, p1Move)` — synthesizes signatures and calls `submitTurnMoves`. +- `_executeBuffered(battleKey)` — calls `executeBatch` for all currently buffered turns. + +New tests: +- **Submission validation**: wrong committer signer, wrong revealer signer (parity), wrong turnId, wrong battleKey, replay, committer preimage hash mismatch, missing committer sig (regression for unilateral-revealer attack), missing revealer sig. +- **Buffer ordering**: out-of-order rejected; batch executes in turnId order. +- **Switch-turn dispatch**: `flag == 0` and `flag == 1` ignore the non-acting half; non-acting player signing a non-NO_OP has no effect. +- **Equivalence (core gate)**: B turns through legacy path vs `submitTurnMoves × B + executeBatch` produce byte-identical state. +- **Game-over short-circuit** mid-batch: remaining stored buffer entries are no longer live after `numTurnsBuffered` resets to zero. +- **Effect lifecycle parity**: BurnStatus DOT over a 4-turn batch matches per-turn execution. +- **Multi-batch in one battle**: submit 4 then execute, submit 4 then execute, submit 6 then execute — `turnId`, `numTurnsBuffered`, and `numTurnsExecuted` advance correctly. +- **Shadow flush**: post-batch `getMonStateForBattle` / `getGlobalKV` / `getEffects` match equivalent per-turn execution. +- **CPU equivalence**: 24-turn CPU game via legacy vs trusted-state batched produces identical end state. + +Existing tests stay untouched — they use the legacy entry points. + +Targeted equivalence tests for v1; differential fuzzing as a follow-up. diff --git a/snapshots/BetterCPUInlineGasTest.json b/snapshots/BetterCPUInlineGasTest.json index ae3a2b0a..36bb56f3 100644 --- a/snapshots/BetterCPUInlineGasTest.json +++ b/snapshots/BetterCPUInlineGasTest.json @@ -1,8 +1,8 @@ { - "Flag0_P0ForcedSwitch": "22701", - "Turn0_Lead": "104410", - "Turn1_BothAttack": "262381", - "Turn2_BothAttack": "236457", - "Turn3_BothAttack": "232481", - "Turn4_BothAttack": "232485" + "Flag0_P0ForcedSwitch": "22814", + "Turn0_Lead": "104286", + "Turn1_BothAttack": "264139", + "Turn2_BothAttack": "238215", + "Turn3_BothAttack": "234239", + "Turn4_BothAttack": "234243" } \ No newline at end of file diff --git a/snapshots/EngineGasTest.json b/snapshots/EngineGasTest.json index 844a9603..8d0b40ce 100644 --- a/snapshots/EngineGasTest.json +++ b/snapshots/EngineGasTest.json @@ -1,21 +1,21 @@ { - "B1_Execute": "889458", - "B1_Setup": "816408", - "B2_Execute": "645719", - "B2_Setup": "282218", - "Battle1_Execute": "449877", - "Battle1_Setup": "791633", - "Battle2_Execute": "367369", - "Battle2_Setup": "233097", - "External_Execute": "456527", - "External_Setup": "782348", - "FirstBattle": "2906000", - "Inline_Execute": "318805", - "Inline_Setup": "225023", + "B1_Execute": "889886", + "B1_Setup": "816459", + "B2_Execute": "648291", + "B2_Setup": "282290", + "Battle1_Execute": "449213", + "Battle1_Setup": "791685", + "Battle2_Execute": "368905", + "Battle2_Setup": "233149", + "External_Execute": "456719", + "External_Setup": "782400", + "FirstBattle": "2911711", + "Inline_Execute": "320015", + "Inline_Setup": "225083", "Intermediary stuff": "44458", - "SecondBattle": "2927592", - "Setup 1": "1670484", - "Setup 2": "292493", - "Setup 3": "332770", - "ThirdBattle": "2274723" + "SecondBattle": "2936650", + "Setup 1": "1670530", + "Setup 2": "292539", + "Setup 3": "332805", + "ThirdBattle": "2282494" } \ No newline at end of file diff --git a/snapshots/EngineOptimizationTest.json b/snapshots/EngineOptimizationTest.json index 883b347d..68dfd453 100644 --- a/snapshots/EngineOptimizationTest.json +++ b/snapshots/EngineOptimizationTest.json @@ -1,4 +1,4 @@ { - "ExternalStaminaRegen": "390772", - "InlineStaminaRegen": "999942" + "ExternalStaminaRegen": "392424", + "InlineStaminaRegen": "999137" } \ No newline at end of file diff --git a/snapshots/FullyOptimizedInlineGasTest.json b/snapshots/FullyOptimizedInlineGasTest.json index 3edf6547..7efaad27 100644 --- a/snapshots/FullyOptimizedInlineGasTest.json +++ b/snapshots/FullyOptimizedInlineGasTest.json @@ -1,8 +1,8 @@ { - "Fast_Battle1": "1802444", - "Fast_Battle2": "1704099", - "Fast_Battle3": "1221705", - "Fast_Setup_1": "1321867", - "Fast_Setup_2": "216314", - "Fast_Setup_3": "212698" + "Fast_Battle1": "1867773", + "Fast_Battle2": "1778380", + "Fast_Battle3": "1288990", + "Fast_Setup_1": "1322085", + "Fast_Setup_2": "216727", + "Fast_Setup_3": "213092" } \ No newline at end of file diff --git a/snapshots/InlineEngineGasTest.json b/snapshots/InlineEngineGasTest.json index ffc94073..5dec063e 100644 --- a/snapshots/InlineEngineGasTest.json +++ b/snapshots/InlineEngineGasTest.json @@ -1,16 +1,16 @@ { - "B1_Execute": "870357", - "B1_Setup": "758497", - "B2_Execute": "604941", - "B2_Setup": "271670", - "Battle1_Execute": "402355", - "Battle1_Setup": "733715", - "Battle2_Execute": "318757", - "Battle2_Setup": "224451", - "FirstBattle": "2595237", - "SecondBattle": "2580140", - "Setup 1": "1612152", - "Setup 2": "318169", - "Setup 3": "314901", - "ThirdBattle": "1964987" + "B1_Execute": "870187", + "B1_Setup": "758557", + "B2_Execute": "606943", + "B2_Setup": "271758", + "Battle1_Execute": "401332", + "Battle1_Setup": "733775", + "Battle2_Execute": "319934", + "Battle2_Setup": "224511", + "FirstBattle": "2596071", + "SecondBattle": "2583719", + "Setup 1": "1612191", + "Setup 2": "318239", + "Setup 3": "314704", + "ThirdBattle": "1967796" } \ No newline at end of file diff --git a/snapshots/MatchmakerTest.json b/snapshots/MatchmakerTest.json index 9b3b0b0c..344f7a99 100644 --- a/snapshots/MatchmakerTest.json +++ b/snapshots/MatchmakerTest.json @@ -1,5 +1,5 @@ { - "Accept1": "307765", - "Accept2": "34130", - "Propose1": "197286" + "Accept1": "307809", + "Accept2": "34162", + "Propose1": "197318" } \ No newline at end of file diff --git a/snapshots/StandardAttackPvPGasTest.json b/snapshots/StandardAttackPvPGasTest.json index 020b58ba..3554d323 100644 --- a/snapshots/StandardAttackPvPGasTest.json +++ b/snapshots/StandardAttackPvPGasTest.json @@ -1,7 +1,7 @@ { - "Turn0_Lead": "64905", - "Turn1_BothAttack": "117165", - "Turn2_BothAttack": "77337", - "Turn3_BothAttack": "77398", - "Turn4_BothAttack": "77347" + "Turn0_Lead": "71464", + "Turn1_BothAttack": "123619", + "Turn2_BothAttack": "83843", + "Turn3_BothAttack": "83869", + "Turn4_BothAttack": "83898" } \ No newline at end of file diff --git a/src/DefaultValidator.sol b/src/DefaultValidator.sol index d2659ed1..e9f35c71 100644 --- a/src/DefaultValidator.sol +++ b/src/DefaultValidator.sol @@ -103,7 +103,7 @@ contract DefaultValidator is IValidator { bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, - uint240 extraData + uint16 extraData ) public view returns (bool) { BattleContext memory ctx = ENGINE.getBattleContext(battleKey); uint256 activeMonIndex = (playerIndex == 0) ? ctx.p0ActiveMonIndex : ctx.p1ActiveMonIndex; @@ -127,7 +127,7 @@ contract DefaultValidator is IValidator { } // Validates that you can't switch to the same mon, you have enough stamina, the move isn't disabled, etc. - function validatePlayerMove(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint240 extraData) + function validatePlayerMove(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint16 extraData) external view returns (bool) @@ -191,7 +191,7 @@ contract DefaultValidator is IValidator { bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, - uint240 extraData, + uint16 extraData, uint256 activeMonIndex, ValidationContext memory vctx ) internal view returns (bool) { diff --git a/src/Engine.sol b/src/Engine.sol index 4f608183..4a42d10d 100644 --- a/src/Engine.sol +++ b/src/Engine.sol @@ -46,8 +46,8 @@ contract Engine is IEngine, MappingAllocator { // A non-zero encoded move is the "transient is populated for this call" signal. uint256 private transient _turnP0MoveEncoded; uint256 private transient _turnP1MoveEncoded; - bytes32 private transient _turnP0Salt; - bytes32 private transient _turnP1Salt; + uint104 private transient _turnP0Salt; + uint104 private transient _turnP1Salt; // Errors error NoWriteAllowed(); @@ -65,7 +65,7 @@ contract Engine is IEngine, MappingAllocator { // Events event BattleStart(bytes32 indexed battleKey, address p0, address p1); event MonMove( - bytes32 indexed battleKey, uint256 packedPlayerIndexMonIndex, uint256 packedMoveIndexExtraData, bytes32 salt + bytes32 indexed battleKey, uint256 packedPlayerIndexMonIndex, uint256 packedMoveIndexExtraData, uint104 salt ); event EngineExecute(bytes32 indexed battleKey); event BattleComplete(bytes32 indexed battleKey, address winner); @@ -300,11 +300,11 @@ contract Engine is IEngine, MappingAllocator { function executeWithMoves( bytes32 battleKey, uint8 p0MoveIndex, - bytes32 p0Salt, - uint240 p0ExtraData, + uint104 p0Salt, + uint16 p0ExtraData, uint8 p1MoveIndex, - bytes32 p1Salt, - uint240 p1ExtraData + uint104 p1Salt, + uint16 p1ExtraData ) external { bytes32 storageKey = _getStorageKey(battleKey); storageKeyForWrite = storageKey; @@ -329,7 +329,7 @@ contract Engine is IEngine, MappingAllocator { /// @notice Combined single-player setMove + execute for forced switch turns /// @dev Only callable by moveManager. The acting player is inferred from battle.playerSwitchForTurnFlag. - function executeWithSingleMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData) external { + function executeWithSingleMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData) external { bytes32 storageKey = _getStorageKey(battleKey); storageKeyForWrite = storageKey; @@ -358,12 +358,12 @@ contract Engine is IEngine, MappingAllocator { _executeInternal(battleKey, storageKey); } - /// @dev Decodes a transient-encoded move (layout: [extraData:240 | packedMoveIndex:8]) into a + /// @dev Decodes a transient-encoded move (layout: [extraData:16 | packedMoveIndex:8]) into a /// MoveDecision. Encoded == 0 means "no current turn move" since packedMoveIndex always has /// IS_REAL_TURN_BIT set for a real move. function _decodeMove(uint256 encoded) private pure returns (MoveDecision memory m) { m.packedMoveIndex = uint8(encoded & 0xFF); - m.extraData = uint240(encoded >> 8); + m.extraData = uint16(encoded >> 8); } /// @dev Returns the current turn's MoveDecision for `playerIndex`. During an active @@ -381,7 +381,7 @@ contract Engine is IEngine, MappingAllocator { } /// @dev Salt companion to `_getCurrentTurnMove`. - function _getCurrentTurnSalt(BattleConfig storage config, uint256 playerIndex) internal view returns (bytes32) { + function _getCurrentTurnSalt(BattleConfig storage config, uint256 playerIndex) internal view returns (uint104) { uint256 encoded = playerIndex == 0 ? _turnP0MoveEncoded : _turnP1MoveEncoded; if (encoded != 0) { return playerIndex == 0 ? _turnP0Salt : _turnP1Salt; @@ -481,12 +481,12 @@ contract Engine is IEngine, MappingAllocator { // Update the temporary RNG to the newest value // Inline RNG computation when oracle is address(0) to avoid external call uint256 rng; - bytes32 p0TurnSalt = _getCurrentTurnSalt(config, 0); - bytes32 p1TurnSalt = _getCurrentTurnSalt(config, 1); + uint104 p0TurnSalt = _getCurrentTurnSalt(config, 0); + uint104 p1TurnSalt = _getCurrentTurnSalt(config, 1); if (address(config.rngOracle) == address(0)) { rng = uint256(keccak256(abi.encode(p0TurnSalt, p1TurnSalt))); } else { - rng = config.rngOracle.getRNG(p0TurnSalt, p1TurnSalt); + rng = config.rngOracle.getRNG(bytes32(uint256(p0TurnSalt)), bytes32(uint256(p1TurnSalt))); } tempRNG = rng; @@ -730,8 +730,8 @@ contract Engine is IEngine, MappingAllocator { function resetCallContext() external { _turnP0MoveEncoded = 0; _turnP1MoveEncoded = 0; - _turnP0Salt = bytes32(0); - _turnP1Salt = bytes32(0); + _turnP0Salt = 0; + _turnP1Salt = 0; } function end(bytes32 battleKey) external { @@ -1377,8 +1377,8 @@ contract Engine is IEngine, MappingAllocator { BattleConfig storage config, uint256 playerIndex, uint8 moveIndex, - bytes32 salt, - uint240 extraData + uint104 salt, + uint16 extraData ) internal { // Pack moveIndex with isRealTurn bit and apply +1 offset for regular moves // Regular moves (< SWITCH_MOVE_INDEX) are stored as moveIndex + 1 to avoid zero ambiguity @@ -1395,7 +1395,7 @@ contract Engine is IEngine, MappingAllocator { } } - function setMove(bytes32 battleKey, uint256 playerIndex, uint8 moveIndex, bytes32 salt, uint240 extraData) + function setMove(bytes32 battleKey, uint256 playerIndex, uint8 moveIndex, uint104 salt, uint16 extraData) external { bool isInsideExecute = _turnP0MoveEncoded != 0 || _turnP1MoveEncoded != 0; @@ -1565,7 +1565,7 @@ contract Engine is IEngine, MappingAllocator { // check below silently no-ops and timeout handles the stuck player. if ((battle.turnId == 0 || currentMonState.isKnockedOut) && moveIndex != SWITCH_MOVE_INDEX) { moveIndex = SWITCH_MOVE_INDEX; - move.extraData = uint240(0); + move.extraData = uint16(0); } // Handle a switch, no-op, or regular move. @@ -2391,7 +2391,7 @@ contract Engine is IEngine, MappingAllocator { /// @notice Validates a player move, handling both inline validation (when validator is address(0)) and external validators /// @dev This allows callers like CPU to validate moves without needing to handle the address(0) case themselves - function validatePlayerMoveForBattle(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint240 extraData) + function validatePlayerMoveForBattle(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint16 extraData) external returns (bool) { diff --git a/src/IEngine.sol b/src/IEngine.sol index 3dd7273f..a75b3e1d 100644 --- a/src/IEngine.sol +++ b/src/IEngine.sol @@ -37,18 +37,18 @@ interface IEngine { uint256 rng ) external returns (int32 damage, bytes32 eventType); function switchActiveMon(uint256 playerIndex, uint256 monToSwitchIndex) external; - function setMove(bytes32 battleKey, uint256 playerIndex, uint8 moveIndex, bytes32 salt, uint240 extraData) external; + function setMove(bytes32 battleKey, uint256 playerIndex, uint8 moveIndex, uint104 salt, uint16 extraData) external; function execute(bytes32 battleKey) external; function executeWithMoves( bytes32 battleKey, uint8 p0MoveIndex, - bytes32 p0Salt, - uint240 p0ExtraData, + uint104 p0Salt, + uint16 p0ExtraData, uint8 p1MoveIndex, - bytes32 p1Salt, - uint240 p1ExtraData + uint104 p1Salt, + uint16 p1ExtraData ) external; - function executeWithSingleMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData) external; + function executeWithSingleMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData) external; function resetCallContext() external; // Getters @@ -94,7 +94,7 @@ interface IEngine { function getPlayerSwitchForTurnFlagForBattleState(bytes32 battleKey) external view returns (uint256); function getGlobalKV(bytes32 battleKey, uint64 key) external view returns (uint192); function getBattleValidator(bytes32 battleKey) external view returns (IValidator); - function validatePlayerMoveForBattle(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint240 extraData) + function validatePlayerMoveForBattle(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint16 extraData) external returns (bool); function getEffects(bytes32 battleKey, uint256 targetIndex, uint256 monIndex) diff --git a/src/IValidator.sol b/src/IValidator.sol index 8dea5f1d..2f71e565 100644 --- a/src/IValidator.sol +++ b/src/IValidator.sol @@ -11,7 +11,7 @@ interface IValidator { ) external returns (bool); // Validates that you can't switch to the same mon, you have enough stamina, the move isn't disabled, etc. - function validatePlayerMove(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint240 extraData) + function validatePlayerMove(bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, uint16 extraData) external returns (bool); @@ -20,7 +20,7 @@ interface IValidator { bytes32 battleKey, uint256 moveIndex, uint256 playerIndex, - uint240 extraData + uint16 extraData ) external returns (bool); // Validates that a switch is valid diff --git a/src/Structs.sol b/src/Structs.sol index 327a52ec..0504a330 100644 --- a/src/Structs.sol +++ b/src/Structs.sol @@ -47,11 +47,11 @@ struct Battle { IEngineHook[] engineHooks; } -// Packed into 1 storage slot (8 + 240 = 248 bits) +// Packed into 1 storage slot (8 + 16 = 24 bits) // packedMoveIndex: lower 7 bits = moveIndex (0-127), bit 7 = isRealTurn (1 = real, 0 = not set) struct MoveDecision { uint8 packedMoveIndex; - uint240 extraData; + uint16 extraData; } // Stored by the Engine, tracks immutable battle data and battle state @@ -81,8 +81,8 @@ struct BattleConfig { uint40 startTimestamp; // 40 — battle start time; overflows in year ~36825 (shrunk from uint48 for slot-2 packing) bool hasInlineStaminaRegen; // 8 uint8 globalKVCount; // 8 — live entry count in the current battle's globalKV key buffer - bytes32 p0Salt; - bytes32 p1Salt; + uint104 p0Salt; + uint104 p1Salt; MoveDecision p0Move; MoveDecision p1Move; mapping(uint256 index => Mon) p0Team; @@ -118,8 +118,8 @@ struct BattleConfigView { uint96 packedP1EffectsCount; uint8 teamSizes; uint40 startTimestamp; // Needed client-side for the getGlobalKV freshness gate - bytes32 p0Salt; - bytes32 p1Salt; + uint104 p0Salt; + uint104 p1Salt; MoveDecision p0Move; MoveDecision p1Move; EffectInstance[] globalEffects; @@ -185,8 +185,8 @@ struct PlayerDecisionData { struct RevealedMove { uint8 moveIndex; - uint240 extraData; - bytes32 salt; + uint16 extraData; + uint104 salt; } // Used for StatBoosts diff --git a/src/commit-manager/DefaultCommitManager.sol b/src/commit-manager/DefaultCommitManager.sol index e2190275..d681cbeb 100644 --- a/src/commit-manager/DefaultCommitManager.sol +++ b/src/commit-manager/DefaultCommitManager.sol @@ -118,7 +118,7 @@ contract DefaultCommitManager is ICommitManager { emit MoveCommit(battleKey, caller); } - function revealMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData, bool autoExecute) + function revealMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData, bool autoExecute) external { // Get all battle context in one call diff --git a/src/commit-manager/ICommitManager.sol b/src/commit-manager/ICommitManager.sol index f9e25bb5..f666c1d8 100644 --- a/src/commit-manager/ICommitManager.sol +++ b/src/commit-manager/ICommitManager.sol @@ -5,7 +5,7 @@ import "../Structs.sol"; interface ICommitManager { function commitMove(bytes32 battleKey, bytes32 moveHash) external; - function revealMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData, bool autoExecute) + function revealMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData, bool autoExecute) external; function getCommitment(bytes32 battleKey, address player) external view returns (bytes32 moveHash, uint256 turnId); function getMoveCountForBattleState(bytes32 battleKey, address player) external view returns (uint256); diff --git a/src/commit-manager/SignedCommitLib.sol b/src/commit-manager/SignedCommitLib.sol index 4646d4ee..3342a749 100644 --- a/src/commit-manager/SignedCommitLib.sol +++ b/src/commit-manager/SignedCommitLib.sol @@ -40,14 +40,14 @@ library SignedCommitLib { uint64 turnId; bytes32 committerMoveHash; // A's hash that B signs over uint8 revealerMoveIndex; - bytes32 revealerSalt; - uint240 revealerExtraData; + uint104 revealerSalt; + uint16 revealerExtraData; } /// @notice Computes the type hash for DualSignedReveal function computeDualSignedRevealTypehash() internal pure returns (bytes32) { return keccak256( - "DualSignedReveal(bytes32 battleKey,uint64 turnId,bytes32 committerMoveHash,uint8 revealerMoveIndex,bytes32 revealerSalt,uint240 revealerExtraData)" + "DualSignedReveal(bytes32 battleKey,uint64 turnId,bytes32 committerMoveHash,uint8 revealerMoveIndex,uint104 revealerSalt,uint16 revealerExtraData)" ); } @@ -58,7 +58,7 @@ library SignedCommitLib { return keccak256( abi.encode( keccak256( - "DualSignedReveal(bytes32 battleKey,uint64 turnId,bytes32 committerMoveHash,uint8 revealerMoveIndex,bytes32 revealerSalt,uint240 revealerExtraData)" + "DualSignedReveal(bytes32 battleKey,uint64 turnId,bytes32 committerMoveHash,uint8 revealerMoveIndex,uint104 revealerSalt,uint16 revealerExtraData)" ), reveal.battleKey, reveal.turnId, diff --git a/src/commit-manager/SignedCommitManager.sol b/src/commit-manager/SignedCommitManager.sol index 20bd4aaf..d12a471e 100644 --- a/src/commit-manager/SignedCommitManager.sol +++ b/src/commit-manager/SignedCommitManager.sol @@ -20,13 +20,16 @@ import {SignedCommitLib} from "./SignedCommitLib.sol"; /// 3. Alice reveals (TX 3) /// /// Dual-signed flow (1 transaction): -/// 1. Alice signs her move hash off-chain, sends to Bob -/// 2. Bob signs his move + Alice's hash off-chain, sends to Alice -/// 3. Alice calls executeWithDualSignedMoves with both moves + Bob's signature (TX 1) +/// 1. Alice signs her move hash off-chain (SignedCommit), sends to Bob +/// 2. Bob signs his move + Alice's hash off-chain (DualSignedReveal), sends back +/// 3. Anyone (Alice, Bob, or a relayer) calls executeWithDualSignedMoves with +/// both signatures + Alice's preimage (TX 1) /// -/// Security: Alice commits to her hash before seeing Bob's move. Bob signs over -/// Alice's hash, so even though Alice sees Bob's move before submitting, Alice -/// cannot change her move (must provide valid preimage for the hash Bob signed). +/// Security: Alice commits to her hash before seeing Bob's move (binding Alice +/// cryptographically via her SignedCommit). Bob signs over Alice's hash (binding +/// Bob via his DualSignedReveal). Both signatures together prove both players' +/// intent without trusting msg.sender — submission can be relayed without +/// reopening any unilateral-revealer attack. /// /// Fallback if Alice stalls: Bob can use commitWithSignature() to publish Alice's /// signed commitment on-chain, then continue with the normal reveal flow. @@ -36,9 +39,6 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { /// @notice Thrown when the signature verification fails error InvalidSignature(); - /// @notice Thrown when caller is not the committing player for this turn - error CallerNotCommitter(); - /// @notice Thrown when trying to use dual-signed flow on a single-player turn error NotTwoPlayerTurn(); @@ -54,8 +54,12 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { } /// @notice Executes a turn using dual-signed moves from both players (gas-optimized) - /// @dev The committer (A) submits both moves. The revealer (B) has signed over - /// their move and A's move hash, binding both players to their moves. + /// @dev Both players sign off-chain — committer over `SignedCommit{committerMoveHash, …}` + /// and revealer over `DualSignedReveal{committerMoveHash, …, revealerMove…}`. Anyone + /// can submit (relayer-friendly) since both signatures are required and bind each + /// player independently. Without the explicit committer signature, a malicious + /// revealer could pick any preimage `P*`, sign `DualSignedReveal{keccak(P*), …}` + /// and play `P*` as the committer's move — the committer signature closes that. /// @param battleKey The battle identifier /// @param committerMoveIndex The committer's move index /// @param committerSalt The committer's salt @@ -63,42 +67,54 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { /// @param revealerMoveIndex The revealer's move index /// @param revealerSalt The revealer's salt /// @param revealerExtraData The revealer's extra data + /// @param committerSignature EIP-712 signature from the committer over + /// SignedCommit(committerMoveHash, battleKey, turnId) /// @param revealerSignature EIP-712 signature from the revealer over - /// DualSignedReveal(battleKey, turnId, committerMoveHash, revealerMove...) + /// DualSignedReveal(battleKey, turnId, committerMoveHash, revealerMove…) function executeWithDualSignedMoves( bytes32 battleKey, uint8 committerMoveIndex, - bytes32 committerSalt, - uint240 committerExtraData, + uint104 committerSalt, + uint16 committerExtraData, uint8 revealerMoveIndex, - bytes32 revealerSalt, - uint240 revealerExtraData, + uint104 revealerSalt, + uint16 revealerExtraData, + bytes calldata committerSignature, bytes calldata revealerSignature ) external { // Use lightweight getter (validates internally, reverts on bad state) (address committer, address revealer, uint64 turnId) = ENGINE.getCommitAuthForDualSigned(battleKey); - // Caller must be the committing player - if (msg.sender != committer) { - revert CallerNotCommitter(); - } - // Compute the committer's move hash bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); + // Verify the committer's signature over SignedCommit + { + SignedCommitLib.SignedCommit memory commit = SignedCommitLib.SignedCommit({ + moveHash: committerMoveHash, + battleKey: battleKey, + turnId: turnId + }); + bytes32 commitDigest = _hashTypedData(SignedCommitLib.hashSignedCommit(commit)); + if (ECDSA.recoverCalldata(commitDigest, committerSignature) != committer) { + revert InvalidSignature(); + } + } + // Verify the revealer's signature over DualSignedReveal - SignedCommitLib.DualSignedReveal memory reveal = SignedCommitLib.DualSignedReveal({ - battleKey: battleKey, - turnId: turnId, - committerMoveHash: committerMoveHash, - revealerMoveIndex: revealerMoveIndex, - revealerSalt: revealerSalt, - revealerExtraData: revealerExtraData - }); - - bytes32 digest = _hashTypedData(SignedCommitLib.hashDualSignedReveal(reveal)); - if (ECDSA.recoverCalldata(digest, revealerSignature) != revealer) { - revert InvalidSignature(); + { + SignedCommitLib.DualSignedReveal memory reveal = SignedCommitLib.DualSignedReveal({ + battleKey: battleKey, + turnId: turnId, + committerMoveHash: committerMoveHash, + revealerMoveIndex: revealerMoveIndex, + revealerSalt: revealerSalt, + revealerExtraData: revealerExtraData + }); + bytes32 revealDigest = _hashTypedData(SignedCommitLib.hashDualSignedReveal(reveal)); + if (ECDSA.recoverCalldata(revealDigest, revealerSignature) != revealer) { + revert InvalidSignature(); + } } // Execute with moves in a single call (engine validates during execution) @@ -130,7 +146,7 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { /// @notice Executes a forced single-player move, usually a switch after a KO, in one transaction. /// @dev The acting player is inferred from the engine's switch flag and must be msg.sender. - function executeSinglePlayerMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData) external { + function executeSinglePlayerMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData) external { CommitContext memory ctx = ENGINE.getCommitContext(battleKey); if (ctx.startTimestamp == 0) { diff --git a/src/cpu/BetterCPU.sol b/src/cpu/BetterCPU.sol index 68077cc9..4a1cbec0 100644 --- a/src/cpu/BetterCPU.sol +++ b/src/cpu/BetterCPU.sol @@ -56,10 +56,10 @@ contract BetterCPU is CPU { // ============ CORE DECISION TREE ============ - function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint240 playerExtraData) + function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint16 playerExtraData) external override - returns (uint128 moveIndex, uint240 extraData) + returns (uint128 moveIndex, uint16 extraData) { (RevealedMove[] memory noOp, RevealedMove[] memory moves, RevealedMove[] memory switches) = _calculateValidMoves(ctx); @@ -380,9 +380,9 @@ contract BetterCPU is CPU { // ============ LEAD SELECTION ============ /// @notice Select lead with dual-type scoring (defensive + offensive) - function _selectLead(bytes32 battleKey, uint240 opponentMonExtraData, RevealedMove[] memory switches) + function _selectLead(bytes32 battleKey, uint16 opponentMonExtraData, RevealedMove[] memory switches) internal - returns (uint128, uint240) + returns (uint128, uint16) { MonStats memory oppStats = ENGINE.getMonStatsForBattle(battleKey, 0, uint256(opponentMonExtraData)); Type oppType1 = oppStats.type1; @@ -445,7 +445,7 @@ contract BetterCPU is CPU { uint256 opponentMonIndex, uint8 opponentMoveIndex, RevealedMove[] memory switches - ) internal view returns (uint128, uint240) { + ) internal view returns (uint128, uint16) { // If opponent isn't attacking, fall back to random if (opponentMoveIndex >= SWITCH_MOVE_INDEX) { return (switches[0].moveIndex, switches[0].extraData); diff --git a/src/cpu/CPU.sol b/src/cpu/CPU.sol index e5553cc8..b48b1a93 100644 --- a/src/cpu/CPU.sol +++ b/src/cpu/CPU.sol @@ -34,10 +34,10 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { * If it's turn 0, randomly selects a mon index to swap to * Otherwise, randomly selects a valid move, switch index, or no op */ - function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint240 playerExtraData) + function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint16 playerExtraData) external virtual - returns (uint128 moveIndex, uint240 extraData); + returns (uint128 moveIndex, uint16 extraData); /** * Public test-friendly wrapper: fetches context and forwards. playerIndex is ignored @@ -63,7 +63,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { uint256 teamSize = ctx.p1TeamSize; RevealedMove[] memory switchChoices = new RevealedMove[](teamSize); for (uint256 i = 0; i < teamSize; i++) { - switchChoices[i] = RevealedMove({moveIndex: SWITCH_MOVE_INDEX, salt: "", extraData: uint240(i)}); + switchChoices[i] = RevealedMove({moveIndex: SWITCH_MOVE_INDEX, salt: 0, extraData: uint16(i)}); } return (new RevealedMove[](0), new RevealedMove[](0), switchChoices); } @@ -77,7 +77,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { validSwitchIndices = new uint256[](teamSize); for (uint256 i = 0; i < teamSize; i++) { if (i != activeMonIndex) { - if (_validateCPUMove(ctx, SWITCH_MOVE_INDEX, uint240(i))) { + if (_validateCPUMove(ctx, SWITCH_MOVE_INDEX, uint16(i))) { validSwitchIndices[validSwitchCount++] = i; } } @@ -89,19 +89,19 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { for (uint256 i = 0; i < validSwitchCount; i++) { switchChoices[i] = RevealedMove({ moveIndex: SWITCH_MOVE_INDEX, - salt: "", - extraData: uint240(validSwitchIndices[i]) + salt: 0, + extraData: uint16(validSwitchIndices[i]) }); } return (new RevealedMove[](0), new RevealedMove[](0), switchChoices); } uint8[] memory validMoveIndices = new uint8[](NUM_MOVES); - uint240[] memory validMoveExtraData = new uint240[](NUM_MOVES); + uint16[] memory validMoveExtraData = new uint16[](NUM_MOVES); uint256 validMoveCount; // Check for valid moves for (uint256 i = 0; i < NUM_MOVES; i++) { uint256 rawMoveSlot = ctx.cpuActiveMonMoveSlots[i]; - uint240 extraDataToUse = 0; + uint16 extraDataToUse = 0; // Inline moves always have ExtraDataType.None — skip extraData logic if (!MoveSlotLib.isInline(rawMoveSlot)) { IMoveSet move = MoveSlotLib.toIMoveSet(rawMoveSlot); @@ -112,7 +112,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { } uint256 randomIndex = _sampleRNG(keccak256(abi.encode(nonce++, ctx.battleKey, block.timestamp))) % validSwitchCount; - extraDataToUse = uint240(validSwitchIndices[randomIndex]); + extraDataToUse = uint16(validSwitchIndices[randomIndex]); validMoveExtraData[validMoveCount] = extraDataToUse; } else if (move.extraDataType() == ExtraDataType.OpponentNonKOTeamIndex) { uint256 opponentTeamSize = ctx.p0TeamSize; @@ -129,7 +129,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { } uint256 randomIndex = _sampleRNG(keccak256(abi.encode(nonce++, ctx.battleKey, block.timestamp))) % validTargetCount; - extraDataToUse = uint240(validTargets[randomIndex]); + extraDataToUse = uint16(validTargets[randomIndex]); validMoveExtraData[validMoveCount] = extraDataToUse; } } @@ -141,15 +141,15 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { RevealedMove[] memory validMovesArray = new RevealedMove[](validMoveCount); for (uint256 i = 0; i < validMoveCount; i++) { validMovesArray[i] = - RevealedMove({moveIndex: validMoveIndices[i], salt: "", extraData: validMoveExtraData[i]}); + RevealedMove({moveIndex: validMoveIndices[i], salt: 0, extraData: validMoveExtraData[i]}); } RevealedMove[] memory validSwitchesArray = new RevealedMove[](validSwitchCount); for (uint256 i = 0; i < validSwitchCount; i++) { validSwitchesArray[i] = - RevealedMove({moveIndex: SWITCH_MOVE_INDEX, salt: "", extraData: uint240(validSwitchIndices[i])}); + RevealedMove({moveIndex: SWITCH_MOVE_INDEX, salt: 0, extraData: uint16(validSwitchIndices[i])}); } RevealedMove[] memory noOpArray = new RevealedMove[](1); - noOpArray[0] = RevealedMove({moveIndex: NO_OP_MOVE_INDEX, salt: "", extraData: 0}); + noOpArray[0] = RevealedMove({moveIndex: NO_OP_MOVE_INDEX, salt: 0, extraData: 0}); nonceToUse = nonce; return (noOpArray, validMovesArray, validSwitchesArray); @@ -172,7 +172,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { /// DEFAULT_MOVES_PER_MON / DEFAULT_MONS_PER_TEAM; the CPU's own iteration already bounds /// moveIndex below NUM_MOVES and monToSwitchIndex below p1TeamSize, so the basic/switch /// checks are equivalent to the engine-side versions. - function _validateCPUMove(CPUContext memory ctx, uint8 moveIndex, uint240 extraData) + function _validateCPUMove(CPUContext memory ctx, uint8 moveIndex, uint16 extraData) internal returns (bool) { @@ -182,7 +182,7 @@ abstract contract CPU is CPUMoveManager, ICPU, ICPURNG, IMatchmaker { return ENGINE.validatePlayerMoveForBattle(ctx.battleKey, moveIndex, 1, extraData); } - function _inlineValidateCPUMove(CPUContext memory ctx, uint8 moveIndex, uint240 extraData) + function _inlineValidateCPUMove(CPUContext memory ctx, uint8 moveIndex, uint16 extraData) private view returns (bool) diff --git a/src/cpu/CPUMoveManager.sol b/src/cpu/CPUMoveManager.sol index 734a03e6..9dcba344 100644 --- a/src/cpu/CPUMoveManager.sol +++ b/src/cpu/CPUMoveManager.sol @@ -22,7 +22,7 @@ abstract contract CPUMoveManager { engine.updateMatchmakers(self, empty); } - function selectMove(bytes32 battleKey, uint8 moveIndex, bytes32 salt, uint240 extraData) external { + function selectMove(bytes32 battleKey, uint8 moveIndex, uint104 salt, uint16 extraData) external { // Cheap routing staticcall: one SLOAD for p0 / winnerIndex / playerSwitchForTurnFlag. // When the turn is "p0 forced switch" (flag == 0) or the game is already over we return // without ever paying for the full CPUContext (which would load team sizes, KO bitmaps, @@ -44,8 +44,8 @@ abstract contract CPUMoveManager { // P1's turn or both players move: CPU calculates its move. Fetch the full context now. CPUContext memory ctx = ENGINE.getCPUContext(battleKey); - (uint128 cpuMoveIndex, uint240 cpuExtraData) = ICPU(address(this)).calculateMove(ctx, moveIndex, extraData); - bytes32 p1Salt = keccak256(abi.encode(battleKey, msg.sender, block.timestamp)); + (uint128 cpuMoveIndex, uint16 cpuExtraData) = ICPU(address(this)).calculateMove(ctx, moveIndex, extraData); + uint104 p1Salt = uint104(uint256(keccak256(abi.encode(battleKey, msg.sender, block.timestamp)))); if (playerSwitchForTurnFlag == 1) { ENGINE.executeWithSingleMove(battleKey, uint8(cpuMoveIndex), p1Salt, cpuExtraData); diff --git a/src/cpu/ICPU.sol b/src/cpu/ICPU.sol index 01e1b381..49e6c1c3 100644 --- a/src/cpu/ICPU.sol +++ b/src/cpu/ICPU.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; import {CPUContext, ProposedBattle} from "../Structs.sol"; interface ICPU { - function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint240 playerExtraData) + function calculateMove(CPUContext memory ctx, uint8 playerMoveIndex, uint16 playerExtraData) external - returns (uint128 moveIndex, uint240 extraData); + returns (uint128 moveIndex, uint16 extraData); function startBattle(ProposedBattle memory proposal) external returns (bytes32 battleKey); } diff --git a/src/cpu/OkayCPU.sol b/src/cpu/OkayCPU.sol index 0b38edd4..c5eccddb 100644 --- a/src/cpu/OkayCPU.sol +++ b/src/cpu/OkayCPU.sol @@ -21,10 +21,10 @@ contract OkayCPU is CPU { /** * If it's turn 0, swap in a mon that resists the other player's type1 (if possible) */ - function calculateMove(CPUContext memory ctx, uint8, uint240) + function calculateMove(CPUContext memory ctx, uint8, uint16) external override - returns (uint128 moveIndex, uint240 extraData) + returns (uint128 moveIndex, uint16 extraData) { (RevealedMove[] memory noOp, RevealedMove[] memory moves, RevealedMove[] memory switches) = _calculateValidMoves(ctx); @@ -126,7 +126,7 @@ contract OkayCPU is CPU { RevealedMove[] memory noOp, RevealedMove[] memory moves, RevealedMove[] memory switches - ) internal returns (uint128, uint240) { + ) internal returns (uint128, uint16) { // Single RNG call - use different bit ranges for different random decisions uint256 rng = _getRNG(battleKey); uint256 movesLen = moves.length; diff --git a/src/cpu/PlayerCPU.sol b/src/cpu/PlayerCPU.sol index 9cb710e4..7c0712e4 100644 --- a/src/cpu/PlayerCPU.sol +++ b/src/cpu/PlayerCPU.sol @@ -13,21 +13,21 @@ contract PlayerCPU is CPU { constructor(uint256 numMoves, IEngine engine, ICPURNG rng) CPU(numMoves, engine, rng) {} - function setMove(bytes32 battleKey, uint8 moveIndex, uint240 extraData) external { + function setMove(bytes32 battleKey, uint8 moveIndex, uint16 extraData) external { if (msg.sender != ENGINE.getPlayersForBattle(battleKey)[0]) { revert NotP0(); } - declaredMoveForBattle[battleKey] = RevealedMove({moveIndex: moveIndex, salt: "", extraData: extraData}); + declaredMoveForBattle[battleKey] = RevealedMove({moveIndex: moveIndex, salt: 0, extraData: extraData}); } /** * Returns the move that p0 declared for this CPU, ignoring the rest of the context. */ - function calculateMove(CPUContext memory ctx, uint8, uint240) + function calculateMove(CPUContext memory ctx, uint8, uint16) external view override - returns (uint128 moveIndex, uint240 extraData) + returns (uint128 moveIndex, uint16 extraData) { return (declaredMoveForBattle[ctx.battleKey].moveIndex, declaredMoveForBattle[ctx.battleKey].extraData); } diff --git a/src/cpu/RandomCPU.sol b/src/cpu/RandomCPU.sol index 83f37d4c..0627d40d 100644 --- a/src/cpu/RandomCPU.sol +++ b/src/cpu/RandomCPU.sol @@ -15,10 +15,10 @@ contract RandomCPU is CPU { * If it's turn 0, randomly selects a mon index to swap to * Otherwise, randomly selects a valid move, switch index, or no op */ - function calculateMove(CPUContext memory ctx, uint8, uint240) + function calculateMove(CPUContext memory ctx, uint8, uint16) external override - returns (uint128 moveIndex, uint240 extraData) + returns (uint128 moveIndex, uint16 extraData) { (RevealedMove[] memory noOp, RevealedMove[] memory moves, RevealedMove[] memory switches) = _calculateValidMoves(ctx); diff --git a/src/effects/status/SleepStatus.sol b/src/effects/status/SleepStatus.sol index 564ae40a..a7e679da 100644 --- a/src/effects/status/SleepStatus.sol +++ b/src/effects/status/SleepStatus.sol @@ -41,7 +41,7 @@ contract SleepStatus is StatusEffect { MoveDecision memory moveDecision = engine.getMoveDecisionForBattleState(battleKey, targetIndex); uint8 moveIndex = moveDecision.packedMoveIndex & MOVE_INDEX_MASK; if (moveIndex != SWITCH_MOVE_INDEX) { - engine.setMove(battleKey, targetIndex, NO_OP_MOVE_INDEX, "", 0); + engine.setMove(battleKey, targetIndex, NO_OP_MOVE_INDEX, 0, 0); } } diff --git a/src/lib/ValidatorLogic.sol b/src/lib/ValidatorLogic.sol index 172229fb..060d9be2 100644 --- a/src/lib/ValidatorLogic.sol +++ b/src/lib/ValidatorLogic.sol @@ -43,7 +43,7 @@ library ValidatorLogic { uint256 rawMoveSlot, uint256 playerIndex, uint256 activeMonIndex, - uint240 extraData, + uint16 extraData, uint32 baseStamina, int32 staminaDelta ) internal view returns (bool valid) { diff --git a/src/mons/aurox/BullRush.sol b/src/mons/aurox/BullRush.sol index 7902cee2..21e747e6 100644 --- a/src/mons/aurox/BullRush.sol +++ b/src/mons/aurox/BullRush.sol @@ -40,7 +40,7 @@ contract BullRush is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) public override { // Deal the damage to opponent diff --git a/src/mons/aurox/GildedRecovery.sol b/src/mons/aurox/GildedRecovery.sol index be90436a..66b939c1 100644 --- a/src/mons/aurox/GildedRecovery.sol +++ b/src/mons/aurox/GildedRecovery.sol @@ -24,10 +24,10 @@ contract GildedRecovery is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240 extraData, + uint16 extraData, uint256 ) external { - // extraData contains the mon index as raw uint240 + // extraData contains the mon index as raw uint16 uint256 targetMonIndex = uint256(extraData); // Check if the target mon has a status effect @@ -84,7 +84,7 @@ contract GildedRecovery is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/aurox/IronWall.sol b/src/mons/aurox/IronWall.sol index 397d3d6c..3c8d807f 100644 --- a/src/mons/aurox/IronWall.sol +++ b/src/mons/aurox/IronWall.sol @@ -25,7 +25,7 @@ contract IronWall is IMoveSet, BasicEffect { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Check to see if the effect is already active @@ -68,7 +68,7 @@ contract IronWall is IMoveSet, BasicEffect { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/aurox/VolatilePunch.sol b/src/mons/aurox/VolatilePunch.sol index fb288cad..772eaa2e 100644 --- a/src/mons/aurox/VolatilePunch.sol +++ b/src/mons/aurox/VolatilePunch.sol @@ -46,7 +46,7 @@ contract VolatilePunch is StandardAttack { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) public override { // Deal the damage to opponent diff --git a/src/mons/ekineki/BubbleBop.sol b/src/mons/ekineki/BubbleBop.sol index cb18cc3f..5aa35f92 100644 --- a/src/mons/ekineki/BubbleBop.sol +++ b/src/mons/ekineki/BubbleBop.sol @@ -39,7 +39,7 @@ contract BubbleBop is StandardAttack { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) public override { uint32 effectiveCritRate = NineNineNineLib._getEffectiveCritRate(engine, battleKey, attackerPlayerIndex); diff --git a/src/mons/ekineki/NineNineNine.sol b/src/mons/ekineki/NineNineNine.sol index 0c1a6f41..614311c5 100644 --- a/src/mons/ekineki/NineNineNine.sol +++ b/src/mons/ekineki/NineNineNine.sol @@ -17,7 +17,7 @@ contract NineNineNine is IMoveSet { return "Nine Nine Nine"; } - function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint240, uint256) external { + function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint16, uint256) external { // Set crit boost for the next turn uint256 currentTurn = engine.getTurnIdForBattleState(battleKey); uint64 key = NineNineNineLib._getKey(attackerPlayerIndex); @@ -40,7 +40,7 @@ contract NineNineNine is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/ekineki/Overflow.sol b/src/mons/ekineki/Overflow.sol index 7d3fa135..f75c87bd 100644 --- a/src/mons/ekineki/Overflow.sol +++ b/src/mons/ekineki/Overflow.sol @@ -39,7 +39,7 @@ contract Overflow is StandardAttack { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) public override { uint32 effectiveCritRate = NineNineNineLib._getEffectiveCritRate(engine, battleKey, attackerPlayerIndex); diff --git a/src/mons/ekineki/SneakAttack.sol b/src/mons/ekineki/SneakAttack.sol index 3c669924..2682e158 100644 --- a/src/mons/ekineki/SneakAttack.sol +++ b/src/mons/ekineki/SneakAttack.sol @@ -35,7 +35,7 @@ contract SneakAttack is IMoveSet, BasicEffect { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240 extraData, + uint16 extraData, uint256 rng ) external { // Check if already used this switch-in (effect present = already used) @@ -107,7 +107,7 @@ contract SneakAttack is IMoveSet, BasicEffect { return MoveClass.Special; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/embursa/HeatBeacon.sol b/src/mons/embursa/HeatBeacon.sol index b6d3f0e2..7d60ab69 100644 --- a/src/mons/embursa/HeatBeacon.sol +++ b/src/mons/embursa/HeatBeacon.sol @@ -28,7 +28,7 @@ contract HeatBeacon is IMoveSet { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 ) external { // Apply burn to opposing mon @@ -56,7 +56,7 @@ contract HeatBeacon is IMoveSet { return Type.Fire; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/embursa/HoneyBribe.sol b/src/mons/embursa/HoneyBribe.sol index a9a68614..653d4d05 100644 --- a/src/mons/embursa/HoneyBribe.sol +++ b/src/mons/embursa/HoneyBribe.sol @@ -51,7 +51,7 @@ contract HoneyBribe is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240, + uint16, uint256 ) external { // Heal active mon by max HP / 2**bribeLevel @@ -110,7 +110,7 @@ contract HoneyBribe is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/embursa/Q5.sol b/src/mons/embursa/Q5.sol index f6bf895a..fb5df4e0 100644 --- a/src/mons/embursa/Q5.sol +++ b/src/mons/embursa/Q5.sol @@ -36,7 +36,7 @@ contract Q5 is IMoveSet, BasicEffect { attackerPlayerIndex = uint256(data) & type(uint128).max; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256, uint16, uint256) external { // Add effect to global effects engine.addEffect(2, attackerPlayerIndex, this, _packExtraData(1, attackerPlayerIndex)); @@ -58,7 +58,7 @@ contract Q5 is IMoveSet, BasicEffect { return Type.Fire; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/embursa/SetAblaze.sol b/src/mons/embursa/SetAblaze.sol index af8ae952..36cdf255 100644 --- a/src/mons/embursa/SetAblaze.sol +++ b/src/mons/embursa/SetAblaze.sol @@ -40,7 +40,7 @@ contract SetAblaze is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 args, + uint16 args, uint256 rng ) public override { engine.dispatchStandardAttack( diff --git a/src/mons/ghouliath/EternalGrudge.sol b/src/mons/ghouliath/EternalGrudge.sol index ad78b202..adecae58 100644 --- a/src/mons/ghouliath/EternalGrudge.sol +++ b/src/mons/ghouliath/EternalGrudge.sol @@ -30,7 +30,7 @@ contract EternalGrudge is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240, + uint16, uint256 ) external { // Apply the debuff (50% debuff to both attack and special attack) @@ -69,7 +69,7 @@ contract EternalGrudge is IMoveSet { return Type.Yin; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/ghouliath/WitherAway.sol b/src/mons/ghouliath/WitherAway.sol index 8115f38c..d8094642 100644 --- a/src/mons/ghouliath/WitherAway.sol +++ b/src/mons/ghouliath/WitherAway.sol @@ -39,7 +39,7 @@ contract WitherAway is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage and inflict panic diff --git a/src/mons/gorillax/RockPull.sol b/src/mons/gorillax/RockPull.sol index 68457ad5..f86b3094 100644 --- a/src/mons/gorillax/RockPull.sol +++ b/src/mons/gorillax/RockPull.sol @@ -40,7 +40,7 @@ contract RockPull is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 rng ) external { if (_didOtherPlayerChooseSwitch(engine, battleKey, attackerPlayerIndex)) { @@ -98,7 +98,7 @@ contract RockPull is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/iblivion/Brightback.sol b/src/mons/iblivion/Brightback.sol index 46f42998..e9712c1c 100644 --- a/src/mons/iblivion/Brightback.sol +++ b/src/mons/iblivion/Brightback.sol @@ -41,7 +41,7 @@ contract Brightback is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 rng ) external { (int32 damageDealt,) = AttackCalculator._calculateDamage( @@ -95,7 +95,7 @@ contract Brightback is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/iblivion/Loop.sol b/src/mons/iblivion/Loop.sol index 8c1b4c2e..6b418083 100644 --- a/src/mons/iblivion/Loop.sol +++ b/src/mons/iblivion/Loop.sol @@ -70,7 +70,7 @@ contract Loop is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Check if Loop is already active @@ -134,7 +134,7 @@ contract Loop is IMoveSet { return Type.Yang; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/iblivion/Renormalize.sol b/src/mons/iblivion/Renormalize.sol index 656dd9f6..81ac7114 100644 --- a/src/mons/iblivion/Renormalize.sol +++ b/src/mons/iblivion/Renormalize.sol @@ -42,7 +42,7 @@ contract Renormalize is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Set Baselight level to 3 @@ -67,7 +67,7 @@ contract Renormalize is IMoveSet { return Type.Yang; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/iblivion/UnboundedStrike.sol b/src/mons/iblivion/UnboundedStrike.sol index 6225859e..f4a3d461 100644 --- a/src/mons/iblivion/UnboundedStrike.sol +++ b/src/mons/iblivion/UnboundedStrike.sol @@ -45,7 +45,7 @@ contract UnboundedStrike is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 rng ) external { uint256 baselightLevel = BASELIGHT.getBaselightLevel(engine, battleKey, attackerPlayerIndex, attackerMonIndex); @@ -95,7 +95,7 @@ contract UnboundedStrike is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/inutia/ChainExpansion.sol b/src/mons/inutia/ChainExpansion.sol index a462fc3f..7d673aad 100644 --- a/src/mons/inutia/ChainExpansion.sol +++ b/src/mons/inutia/ChainExpansion.sol @@ -33,7 +33,7 @@ contract ChainExpansion is IMoveSet, BasicEffect { return keccak256(abi.encode(playerIndex, monIndex, name())); } - function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint240, uint256) external { + function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint16, uint256) external { // Check if the ability is already applied globally (EffectInstance[] memory effects, ) = engine.getEffects(battleKey, 2, 2); for (uint256 i = 0; i < effects.length; i++) { @@ -61,7 +61,7 @@ contract ChainExpansion is IMoveSet, BasicEffect { return MoveClass.Other; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/inutia/HitAndDip.sol b/src/mons/inutia/HitAndDip.sol index 36357b77..dc087ec4 100644 --- a/src/mons/inutia/HitAndDip.sol +++ b/src/mons/inutia/HitAndDip.sol @@ -39,7 +39,7 @@ contract HitAndDip is StandardAttack { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage @@ -51,7 +51,7 @@ contract HitAndDip is StandardAttack { ); if (damage > 0) { - // extraData contains the swap index as raw uint240 + // extraData contains the swap index as raw uint16 uint256 swapIndex = uint256(extraData); engine.switchActiveMon(attackerPlayerIndex, swapIndex); } diff --git a/src/mons/inutia/Initialize.sol b/src/mons/inutia/Initialize.sol index 427147c0..d9fb1cfe 100644 --- a/src/mons/inutia/Initialize.sol +++ b/src/mons/inutia/Initialize.sol @@ -35,7 +35,7 @@ contract Initialize is IMoveSet, BasicEffect { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Check if global KV is set @@ -83,7 +83,7 @@ contract Initialize is IMoveSet, BasicEffect { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/malalien/TripleThink.sol b/src/mons/malalien/TripleThink.sol index 95a24830..5b0a8d0b 100644 --- a/src/mons/malalien/TripleThink.sol +++ b/src/mons/malalien/TripleThink.sol @@ -29,7 +29,7 @@ contract TripleThink is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Apply the buff @@ -58,7 +58,7 @@ contract TripleThink is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/pengym/Deadlift.sol b/src/mons/pengym/Deadlift.sol index 120974e0..065843f3 100644 --- a/src/mons/pengym/Deadlift.sol +++ b/src/mons/pengym/Deadlift.sol @@ -30,7 +30,7 @@ contract Deadlift is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { // Apply the buffs @@ -64,7 +64,7 @@ contract Deadlift is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/pengym/DeepFreeze.sol b/src/mons/pengym/DeepFreeze.sol index 6f841a39..9dba3bd8 100644 --- a/src/mons/pengym/DeepFreeze.sol +++ b/src/mons/pengym/DeepFreeze.sol @@ -47,7 +47,7 @@ contract DeepFreeze is IMoveSet { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) external { uint256 otherPlayerIndex = (attackerPlayerIndex + 1) % 2; @@ -90,7 +90,7 @@ contract DeepFreeze is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/pengym/PistolSquat.sol b/src/mons/pengym/PistolSquat.sol index 9bb24bee..b234624d 100644 --- a/src/mons/pengym/PistolSquat.sol +++ b/src/mons/pengym/PistolSquat.sol @@ -60,7 +60,7 @@ contract PistolSquat is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage diff --git a/src/mons/sofabbi/Gachachacha.sol b/src/mons/sofabbi/Gachachacha.sol index a25180f8..1d49a9bb 100644 --- a/src/mons/sofabbi/Gachachacha.sol +++ b/src/mons/sofabbi/Gachachacha.sol @@ -41,7 +41,7 @@ contract Gachachacha is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) external { uint256 chance = rng % OPP_KO_THRESHOLD_R; @@ -91,7 +91,7 @@ contract Gachachacha is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/sofabbi/GuestFeature.sol b/src/mons/sofabbi/GuestFeature.sol index 83fb7d6a..1b73f2f9 100644 --- a/src/mons/sofabbi/GuestFeature.sol +++ b/src/mons/sofabbi/GuestFeature.sol @@ -30,7 +30,7 @@ contract GuestFeature is IMoveSet { uint256 attackerPlayerIndex, uint256, uint256, - uint240 extraData, + uint16 extraData, uint256 rng ) external { uint256 monIndex = uint256(extraData); @@ -67,7 +67,7 @@ contract GuestFeature is IMoveSet { return MoveClass.Physical; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/sofabbi/SnackBreak.sol b/src/mons/sofabbi/SnackBreak.sol index ec40b197..7b78752b 100644 --- a/src/mons/sofabbi/SnackBreak.sol +++ b/src/mons/sofabbi/SnackBreak.sol @@ -42,7 +42,7 @@ contract SnackBreak is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { uint256 snackLevel = _getSnackLevel(engine, battleKey, attackerPlayerIndex, attackerMonIndex); @@ -78,7 +78,7 @@ contract SnackBreak is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/volthare/DualShock.sol b/src/mons/volthare/DualShock.sol index be91b0fc..bc541f89 100644 --- a/src/mons/volthare/DualShock.sol +++ b/src/mons/volthare/DualShock.sol @@ -45,7 +45,7 @@ contract DualShock is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage diff --git a/src/mons/volthare/MegaStarBlast.sol b/src/mons/volthare/MegaStarBlast.sol index 9ff0f9de..df7c0aa7 100644 --- a/src/mons/volthare/MegaStarBlast.sol +++ b/src/mons/volthare/MegaStarBlast.sol @@ -52,7 +52,7 @@ contract MegaStarBlast is IMoveSet { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) external { // Check if Overclock is active @@ -104,7 +104,7 @@ contract MegaStarBlast is IMoveSet { return MoveClass.Special; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/volthare/RoundTrip.sol b/src/mons/volthare/RoundTrip.sol index 8880e455..1fa154fe 100644 --- a/src/mons/volthare/RoundTrip.sol +++ b/src/mons/volthare/RoundTrip.sol @@ -39,7 +39,7 @@ contract RoundTrip is StandardAttack { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage @@ -51,7 +51,7 @@ contract RoundTrip is StandardAttack { ); if (damage > 0) { - // extraData contains the swap index as raw uint240 + // extraData contains the swap index as raw uint16 uint256 swapIndex = uint256(extraData); engine.switchActiveMon(attackerPlayerIndex, swapIndex); } diff --git a/src/mons/xmon/ContagiousSlumber.sol b/src/mons/xmon/ContagiousSlumber.sol index 5153ae73..5c349615 100644 --- a/src/mons/xmon/ContagiousSlumber.sol +++ b/src/mons/xmon/ContagiousSlumber.sol @@ -27,7 +27,7 @@ contract ContagiousSlumber is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240, + uint16, uint256 ) external { // Apply sleep to self @@ -54,7 +54,7 @@ contract ContagiousSlumber is IMoveSet { return MoveClass.Other; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/xmon/NightTerrors.sol b/src/mons/xmon/NightTerrors.sol index 399b053e..fb7984a7 100644 --- a/src/mons/xmon/NightTerrors.sol +++ b/src/mons/xmon/NightTerrors.sol @@ -36,7 +36,7 @@ contract NightTerrors is IMoveSet, BasicEffect { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256, - uint240, + uint16, uint256 ) external { uint256 defenderPlayerIndex = (attackerPlayerIndex + 1) % 2; @@ -96,7 +96,7 @@ contract NightTerrors is IMoveSet, BasicEffect { return MoveClass.Special; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/xmon/Somniphobia.sol b/src/mons/xmon/Somniphobia.sol index 7916a432..628e3553 100644 --- a/src/mons/xmon/Somniphobia.sol +++ b/src/mons/xmon/Somniphobia.sol @@ -18,7 +18,7 @@ contract Somniphobia is IMoveSet, BasicEffect { return "Somniphobia"; } - function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint240, uint256) external { + function move(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex, uint256, uint256, uint16, uint256) external { // Add effect globally for 6 turns (only if it's not already in global effects) (EffectInstance[] memory effects, ) = engine.getEffects(battleKey, 2, 2); for (uint256 i = 0; i < effects.length; i++) { @@ -45,7 +45,7 @@ contract Somniphobia is IMoveSet, BasicEffect { return MoveClass.Other; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/src/mons/xmon/VitalSiphon.sol b/src/mons/xmon/VitalSiphon.sol index d2f472d7..1f85f0d8 100644 --- a/src/mons/xmon/VitalSiphon.sol +++ b/src/mons/xmon/VitalSiphon.sol @@ -40,7 +40,7 @@ contract VitalSiphon is StandardAttack { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) public override { // Deal the damage diff --git a/src/moves/IMoveSet.sol b/src/moves/IMoveSet.sol index 3ca381fe..cba4b69b 100644 --- a/src/moves/IMoveSet.sol +++ b/src/moves/IMoveSet.sol @@ -13,7 +13,7 @@ interface IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) external; function priority(IEngine engine, bytes32 battleKey, uint256 attackerPlayerIndex) external view returns (uint32); @@ -22,7 +22,7 @@ interface IMoveSet { view returns (uint32); function moveType(IEngine engine, bytes32 battleKey) external view returns (Type); - function isValidTarget(IEngine engine, bytes32 battleKey, uint240 extraData) external view returns (bool); + function isValidTarget(IEngine engine, bytes32 battleKey, uint16 extraData) external view returns (bool); function moveClass(IEngine engine, bytes32 battleKey) external view returns (MoveClass); function extraDataType() external view returns (ExtraDataType); diff --git a/src/moves/StandardAttack.sol b/src/moves/StandardAttack.sol index 47bc9f51..66a464d6 100644 --- a/src/moves/StandardAttack.sol +++ b/src/moves/StandardAttack.sol @@ -51,7 +51,7 @@ contract StandardAttack is IMoveSet, Ownable { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240, + uint16, uint256 rng ) public virtual { _move(engine, battleKey, attackerPlayerIndex, defenderMonIndex, rng); @@ -83,7 +83,7 @@ contract StandardAttack is IMoveSet, Ownable { ); } - function isValidTarget(IEngine, bytes32, uint240) public pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) public pure returns (bool) { return true; } diff --git a/test/BatchInstrumentationTest.sol b/test/BatchInstrumentationTest.sol new file mode 100644 index 00000000..bbd26dbb --- /dev/null +++ b/test/BatchInstrumentationTest.sol @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import "../lib/forge-std/src/Test.sol"; +import "../src/Constants.sol"; +import "../src/Enums.sol"; +import "../src/Structs.sol"; + +import {Engine} from "../src/Engine.sol"; +import {SignedCommitManager} from "../src/commit-manager/SignedCommitManager.sol"; +import {SignedCommitLib} from "../src/commit-manager/SignedCommitLib.sol"; +import {SignedMatchmaker} from "../src/matchmaker/SignedMatchmaker.sol"; +import {BattleOfferLib} from "../src/matchmaker/BattleOfferLib.sol"; +import {StandardAttackFactory} from "../src/moves/StandardAttackFactory.sol"; +import {ATTACK_PARAMS} from "../src/moves/StandardAttackStructs.sol"; +import {EIP712} from "../src/lib/EIP712.sol"; + +import {IEngine} from "../src/IEngine.sol"; +import {IEngineHook} from "../src/IEngineHook.sol"; +import {IEffect} from "../src/effects/IEffect.sol"; +import {IMoveSet} from "../src/moves/IMoveSet.sol"; +import {IRandomnessOracle} from "../src/rng/IRandomnessOracle.sol"; +import {IRuleset} from "../src/IRuleset.sol"; +import {IValidator} from "../src/IValidator.sol"; + +import {TypeCalculator} from "../src/types/TypeCalculator.sol"; +import {ITypeCalculator} from "../src/types/ITypeCalculator.sol"; + +import {TestTeamRegistry} from "./mocks/TestTeamRegistry.sol"; + +/// Counts SLOAD / SSTORE access patterns on a warm steady-state turn, to ground the PLAN_OPT.md +/// gas math in real data instead of estimates. +contract BatchInstrumentationTest is Test, EIP712 { + + uint256 constant MONS_PER_TEAM = 4; + uint256 constant MOVES_PER_MON = 4; + + uint256 constant P0_PK = 0xA11CE; + uint256 constant P1_PK = 0xB0B; + address p0; + address p1; + + Engine engine; + SignedCommitManager signedCommitManager; + SignedMatchmaker signedMatchmaker; + ITypeCalculator typeCalc; + TestTeamRegistry defaultRegistry; + StandardAttackFactory attackFactory; + + function _domainNameAndVersion() internal pure override returns (string memory, string memory) { + return ("SignedCommitManager", "1"); + } + + function setUp() public { + p0 = vm.addr(P0_PK); + p1 = vm.addr(P1_PK); + + engine = new Engine(MONS_PER_TEAM, MOVES_PER_MON, 1); + signedCommitManager = new SignedCommitManager(IEngine(address(engine))); + signedMatchmaker = new SignedMatchmaker(engine); + typeCalc = new TypeCalculator(); + defaultRegistry = new TestTeamRegistry(); + attackFactory = new StandardAttackFactory(typeCalc); + } + + function _startBattle(IRuleset ruleset) internal returns (bytes32) { + address[] memory makersToAdd = new address[](1); + makersToAdd[0] = address(signedMatchmaker); + address[] memory makersToRemove = new address[](0); + vm.prank(p0); + engine.updateMatchmakers(makersToAdd, makersToRemove); + vm.prank(p1); + engine.updateMatchmakers(makersToAdd, makersToRemove); + + (bytes32 battleKey, bytes32 pairHash) = engine.computeBattleKey(p0, p1); + uint256 nonce = engine.pairHashNonces(pairHash); + + BattleOffer memory offer = BattleOffer({ + battle: Battle({ + p0: p0, + p0TeamIndex: 0, + p1: p1, + p1TeamIndex: 0, + teamRegistry: defaultRegistry, + validator: IValidator(address(0)), + rngOracle: IRandomnessOracle(address(0)), + ruleset: ruleset, + moveManager: address(signedCommitManager), + matchmaker: signedMatchmaker, + engineHooks: new IEngineHook[](0) + }), + pairHashNonce: nonce + }); + + bytes32 structHash = BattleOfferLib.hashBattleOffer(offer); + bytes32 digest = signedMatchmaker.hashTypedData(structHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(P0_PK, digest); + bytes memory signature = abi.encodePacked(r, s, v); + + vm.prank(p1); + signedMatchmaker.startGame(offer, signature); + + return battleKey; + } + + function _signDualReveal( + uint256 privateKey, + bytes32 battleKey, + uint64 turnId, + bytes32 committerMoveHash, + uint8 revealerMoveIndex, + uint104 revealerSalt, + uint16 revealerExtraData + ) internal view returns (bytes memory) { + bytes32 domainSeparator = keccak256( + abi.encode( + _DOMAIN_TYPEHASH, + keccak256("SignedCommitManager"), + keccak256("1"), + block.chainid, + address(signedCommitManager) + ) + ); + bytes32 structHash = SignedCommitLib.hashDualSignedReveal( + SignedCommitLib.DualSignedReveal({ + battleKey: battleKey, + turnId: turnId, + committerMoveHash: committerMoveHash, + revealerMoveIndex: revealerMoveIndex, + revealerSalt: revealerSalt, + revealerExtraData: revealerExtraData + }) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) + internal + view + returns (bytes memory) + { + bytes32 domainSeparator = keccak256( + abi.encode( + _DOMAIN_TYPEHASH, + keccak256("SignedCommitManager"), + keccak256("1"), + block.chainid, + address(signedCommitManager) + ) + ); + bytes32 structHash = SignedCommitLib.hashSignedCommit( + SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function _fastTurn( + bytes32 battleKey, + uint8 p0MoveIndex, + uint8 p1MoveIndex, + uint16 p0ExtraData, + uint16 p1ExtraData + ) internal { + uint64 turnId = uint64(engine.getTurnIdForBattleState(battleKey)); + uint104 committerSalt = uint104(uint256(keccak256(abi.encode("committer", battleKey, turnId)))); + uint104 revealerSalt = uint104(uint256(keccak256(abi.encode("revealer", battleKey, turnId)))); + + uint8 committerMoveIndex; + uint16 committerExtraData; + uint8 revealerMoveIndex; + uint16 revealerExtraData; + uint256 committerPk; + uint256 revealerPk; + address committer; + + if (turnId % 2 == 0) { + committerMoveIndex = p0MoveIndex; + committerExtraData = p0ExtraData; + revealerMoveIndex = p1MoveIndex; + revealerExtraData = p1ExtraData; + committerPk = P0_PK; + revealerPk = P1_PK; + committer = p0; + } else { + committerMoveIndex = p1MoveIndex; + committerExtraData = p1ExtraData; + revealerMoveIndex = p0MoveIndex; + revealerExtraData = p0ExtraData; + committerPk = P1_PK; + revealerPk = P0_PK; + committer = p1; + } + + bytes32 committerMoveHash = + keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); + bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); + bytes memory revealerSig = _signDualReveal( + revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData + ); + + vm.prank(committer); + signedCommitManager.executeWithDualSignedMoves( + battleKey, + committerMoveIndex, committerSalt, committerExtraData, + revealerMoveIndex, revealerSalt, revealerExtraData, + committerSig, revealerSig + ); + engine.resetCallContext(); + } + + function _createMon(Type t1) internal pure returns (Mon memory) { + return Mon({ + stats: MonStats({ + hp: 10000, + stamina: 50, + speed: 10, + attack: 30, + defense: 10, + specialAttack: 30, + specialDefense: 10, + type1: t1, + type2: Type.None + }), + moves: new uint256[](0), + ability: 0 + }); + } + + /// @dev Iterates account accesses returned by stopAndReturnStateDiff and counts SLOAD/SSTORE + /// per (account, slot) — distinguishing first-touch (cold) from subsequent (warm), and + /// for SSTORE distinguishing zero→nonzero / nonzero→nonzero / no-op. + function _summarizeAccesses(Vm.AccountAccess[] memory accesses) + internal + pure + returns ( + uint256 totalSloadCount, + uint256 totalSstoreCount, + uint256 coldSloads, + uint256 warmSloads, + uint256 coldSstores, + uint256 warmSstores, + uint256 zeroToNonzeroSstores, + uint256 nonzeroToNonzeroSstores, + uint256 noopSstores, + uint256 uniqueSlotsTouched, + uint256 multiWriteSlots + ) + { + // Count slot-touch frequencies via a small fixed-capacity table (we don't expect many uniques) + bytes32[] memory keys = new bytes32[](256); + uint8[] memory writes = new uint8[](256); + bool[] memory reads = new bool[](256); + uint256 keyCount; + + for (uint256 i = 0; i < accesses.length; i++) { + Vm.StorageAccess[] memory storageAccesses = accesses[i].storageAccesses; + for (uint256 j = 0; j < storageAccesses.length; j++) { + Vm.StorageAccess memory a = storageAccesses[j]; + bytes32 key = keccak256(abi.encode(a.account, a.slot)); + + // Locate or create entry + uint256 idx = keyCount; + for (uint256 k = 0; k < keyCount; k++) { + if (keys[k] == key) { + idx = k; + break; + } + } + if (idx == keyCount) { + keys[idx] = key; + keyCount++; + } + + if (a.isWrite) { + totalSstoreCount++; + writes[idx]++; + if (a.previousValue == bytes32(0) && a.newValue != bytes32(0)) zeroToNonzeroSstores++; + else if (a.previousValue != bytes32(0) && a.newValue != bytes32(0) && a.previousValue != a.newValue) + nonzeroToNonzeroSstores++; + else if (a.previousValue == a.newValue) noopSstores++; + + if (writes[idx] == 1 && !reads[idx]) { + coldSstores++; + } else { + warmSstores++; + } + } else { + totalSloadCount++; + if (!reads[idx] && writes[idx] == 0) { + coldSloads++; + reads[idx] = true; + } else { + warmSloads++; + } + } + } + } + + uniqueSlotsTouched = keyCount; + for (uint256 i = 0; i < keyCount; i++) { + if (writes[i] >= 2) multiWriteSlots++; + } + } + + /// @notice Per-turn storage-access profile for a clean PvP damage-trade turn (steady state). + function test_storageAccessProfile_cleanDamageTradeTurn() public { + IMoveSet moveA = attackFactory.createAttack( + ATTACK_PARAMS({ + BASE_POWER: 30, STAMINA_COST: 1, ACCURACY: 100, PRIORITY: 1, + MOVE_TYPE: Type.Fire, EFFECT_ACCURACY: 0, MOVE_CLASS: MoveClass.Physical, + CRIT_RATE: 0, VOLATILITY: 0, NAME: "AttackA", EFFECT: IEffect(address(0)) + }) + ); + IMoveSet moveB = attackFactory.createAttack( + ATTACK_PARAMS({ + BASE_POWER: 25, STAMINA_COST: 1, ACCURACY: 100, PRIORITY: 1, + MOVE_TYPE: Type.Fire, EFFECT_ACCURACY: 0, MOVE_CLASS: MoveClass.Special, + CRIT_RATE: 0, VOLATILITY: 0, NAME: "AttackB", EFFECT: IEffect(address(0)) + }) + ); + + Mon memory mon = _createMon(Type.Fire); + mon.moves = new uint256[](MOVES_PER_MON); + mon.moves[0] = uint256(uint160(address(moveA))); + mon.moves[1] = uint256(uint160(address(moveB))); + mon.moves[2] = uint256(uint160(address(moveA))); + mon.moves[3] = uint256(uint160(address(moveB))); + + Mon[] memory team = new Mon[](MONS_PER_TEAM); + for (uint256 i; i < MONS_PER_TEAM; i++) team[i] = mon; + defaultRegistry.setTeam(p0, team); + defaultRegistry.setTeam(p1, team); + + IRuleset ruleset = IRuleset(INLINE_STAMINA_REGEN_RULESET); + bytes32 battleKey = _startBattle(ruleset); + vm.warp(vm.getBlockTimestamp() + 1); + + // Warm-up: lead-in switch + 1 damage trade. + _fastTurn(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); + _fastTurn(battleKey, 0, 0, 0, 0); + + // Now profile a steady-state warm turn. + vm.startStateDiffRecording(); + _fastTurn(battleKey, 1, 1, 0, 0); + Vm.AccountAccess[] memory diffs = vm.stopAndReturnStateDiff(); + + ( + uint256 totalSload, + uint256 totalSstore, + uint256 coldSload, + uint256 warmSload, + uint256 coldSstore, + uint256 warmSstore, + uint256 z2nz, + uint256 nz2nz, + uint256 noop, + uint256 unique, + uint256 multiWrite + ) = _summarizeAccesses(diffs); + + console.log("=== CLEAN DAMAGE-TRADE TURN - STORAGE PROFILE ==="); + console.log("Total SLOADs :", totalSload); + console.log(" Cold (first-touch in tx) :", coldSload); + console.log(" Warm :", warmSload); + console.log("Total SSTOREs :", totalSstore); + console.log(" Cold (first-touch in tx) :", coldSstore); + console.log(" Warm :", warmSstore); + console.log(" zero -> nonzero :", z2nz); + console.log(" nonzero -> nonzero (diff) :", nz2nz); + console.log(" no-op (same value) :", noop); + console.log("Unique slots touched :", unique); + console.log("Slots written 2+ times in turn :", multiWrite); + } +} diff --git a/test/BattleHistoryTest.sol b/test/BattleHistoryTest.sol index 005af49a..2aed9ea2 100644 --- a/test/BattleHistoryTest.sol +++ b/test/BattleHistoryTest.sol @@ -166,10 +166,10 @@ contract BattleHistoryTest is Test, BattleHelper { address p1, uint8 p0MoveIndex, uint8 p1MoveIndex, - uint240 p0ExtraData, - uint240 p1ExtraData + uint16 p0ExtraData, + uint16 p1ExtraData ) internal { - bytes32 salt = ""; + uint104 salt = 0; bytes32 p0MoveHash = keccak256(abi.encodePacked(p0MoveIndex, salt, p0ExtraData)); bytes32 p1MoveHash = keccak256(abi.encodePacked(p1MoveIndex, salt, p1ExtraData)); @@ -199,7 +199,7 @@ contract BattleHistoryTest is Test, BattleHelper { // First move - both players switch to their mon _commitRevealExecute( - battleKey, battleData.p0, battleData.p1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + battleKey, battleData.p0, battleData.p1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Second move - both attack (faster mon wins) @@ -216,7 +216,7 @@ contract BattleHistoryTest is Test, BattleHelper { assertEq(battleHistory.getNumBattles(BOB), 0, "Bob should have 0 battles before completion"); // First turn - both switch to their mons - _commitRevealExecute(battleKey, ALICE, BOB, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecute(battleKey, ALICE, BOB, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Stats should still be 0 after first turn assertEq(battleHistory.getNumBattles(ALICE), 0, "Alice should have 0 battles after switch"); diff --git a/test/BetterCPUInlineGasTest.sol b/test/BetterCPUInlineGasTest.sol index 9cccd4e5..e7b4112e 100644 --- a/test/BetterCPUInlineGasTest.sol +++ b/test/BetterCPUInlineGasTest.sol @@ -159,28 +159,28 @@ contract BetterCPUInlineGasTest is Test { // Turn 0: lead selection. Both players "switch in" a starting mon. vm.startSnapshotGas("Turn0_Lead"); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); uint256 turn0Gas = vm.stopSnapshotGas("Turn0_Lead"); engine.resetCallContext(); // Turns 1-4: both attack with move 1. Every one is flag == 2, no KOs. vm.startSnapshotGas("Turn1_BothAttack"); - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); uint256 turn1Gas = vm.stopSnapshotGas("Turn1_BothAttack"); engine.resetCallContext(); vm.startSnapshotGas("Turn2_BothAttack"); - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); uint256 turn2Gas = vm.stopSnapshotGas("Turn2_BothAttack"); engine.resetCallContext(); vm.startSnapshotGas("Turn3_BothAttack"); - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); uint256 turn3Gas = vm.stopSnapshotGas("Turn3_BothAttack"); engine.resetCallContext(); vm.startSnapshotGas("Turn4_BothAttack"); - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); uint256 turn4Gas = vm.stopSnapshotGas("Turn4_BothAttack"); engine.resetCallContext(); @@ -225,11 +225,11 @@ contract BetterCPUInlineGasTest is Test { mockCPURNG.setRNG(0); // Turn 0: lead. - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: both attack. CPU's move 1 (BP=40, attack=200, defense=10) should KO Alice. - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); engine.resetCallContext(); // After the KO we should be in flag==0 (Alice forced switch). @@ -238,7 +238,7 @@ contract BetterCPUInlineGasTest is Test { // Measure the cheap-router path: Alice submits her switch via the CPU manager. vm.startSnapshotGas("Flag0_P0ForcedSwitch"); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1)); uint256 flag0Gas = vm.stopSnapshotGas("Flag0_P0ForcedSwitch"); engine.resetCallContext(); diff --git a/test/BetterCPUTest.sol b/test/BetterCPUTest.sol index e331a72a..f825a424 100644 --- a/test/BetterCPUTest.sol +++ b/test/BetterCPUTest.sol @@ -198,13 +198,13 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Turn 0: Both select mon 0 - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Deal some damage to Alice's mon so CPU sees it can KO // The CPU should select the high power move (index 1) to secure the KO // Set RNG to not trigger random selection mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Check that Alice's mon took massive damage (from high power attack) int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); @@ -243,7 +243,7 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Alice selects mon 0 (Fire type) - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // CPU should have selected Liquid mon (index 1) uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); @@ -298,7 +298,7 @@ contract BetterCPUTest is Test { // Since neither mon resists Liquid (Alice's lead could be Liquid), CPU picks randomly // We force Fire mon to be selected mockCPURNG.setRNG(0); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Get active mons - CPU might have selected based on type calcs uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); @@ -306,7 +306,7 @@ contract BetterCPUTest is Test { // Turn 1: CPU should detect kill threat from Fire attack and switch to Liquid if currently Fire mockCPURNG.setRNG(1); // Don't trigger random selection - cpu.selectMove(battleKey, 0, "", 0); // Alice attacks + cpu.selectMove(battleKey, 0, uint104(0), 0); // Alice attacks // If CPU started with Fire, it should switch to Liquid to survive // If CPU started with Liquid, it should stay (already resists Fire) @@ -354,12 +354,12 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(team, team); // Turn 0: Both select mon 0 - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Use the expensive attack (costs 5 stamina) // RNG = 1 won't trigger random selection (1 % 10 != 0) mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // Stamina delta should be -5 int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -367,7 +367,7 @@ contract BetterCPUTest is Test { // Turn 2: Opponent rests (P4 path). New BetterCPU attacks on free turns even at low stamina. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Stamina should be -10 (attacked again with the 5-cost move on the free turn) staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -409,12 +409,12 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(team, team); // Turn 0: Both select mon 0 - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: At full HP, CPU should prefer setup move // Set RNG to not trigger random selection mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Check stamina consumed (setup move costs 1) int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -434,28 +434,36 @@ contract BetterCPUTest is Test { moves[1] = uint256(uint160(address(mediumAttack))); moves[2] = uint256(uint160(address(strongAttack))); - Mon memory mon = _createMon(Type.Fire, 100, 50, 10); - mon.moves = moves; + // CPU mon is faster than Alice's so attack ordering is deterministic regardless + // of the engine's RNG-based speed-tie breaker. + Mon memory aliceMon = _createMon(Type.Fire, 100, 50, 10); + aliceMon.moves = moves; + Mon memory cpuMon = _createMon(Type.Fire, 100, 50, 10); + cpuMon.stats.speed = 100; + cpuMon.moves = moves; // Need 2 mons per team - Mon[] memory team = new Mon[](2); - team[0] = mon; - team[1] = mon; + Mon[] memory aliceTeam = new Mon[](2); + aliceTeam[0] = aliceMon; + aliceTeam[1] = aliceMon; + Mon[] memory cpuTeam = new Mon[](2); + cpuTeam[0] = cpuMon; + cpuTeam[1] = cpuMon; - bytes32 battleKey = _startBattleWithCPU(team, team); + bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Turn 0: Both select mon 0 - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU is at full HP, so attack first with Alice to damage CPU // Then CPU will be at non-full HP and prefer attack moves mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 2, "", 0); // Alice uses strong attack on CPU + cpu.selectMove(battleKey, 2, uint104(0), 0); // Alice uses strong attack on CPU // Now CPU's HP is damaged, next turn it should use highest damage move // Turn 2: CPU should select the strongest attack mockCPURNG.setRNG(1); // Don't trigger random - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Verify significant damage was dealt (strong attack) - Alice took damage both turns int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); @@ -492,7 +500,7 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Alice selects mon 0 (Fire/Nature) - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should select Liquid mon (resists both Fire and Nature)"); @@ -521,7 +529,7 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Fire, Type.Nature, 20); // 2x bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should select Fire mon (2x offensive vs Nature)"); @@ -546,7 +554,7 @@ contract BetterCPUTest is Test { } bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Just verify no crash and valid index uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); @@ -601,7 +609,7 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Turn 0: lead selection. All neutral → picks mon 0 (first). - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should lead with mon 0"); @@ -609,12 +617,12 @@ contract BetterCPUTest is Test { // Turn 1: Alice uses Fire move (move 0). All CPU mons take equal Fire damage. // P5 materiality fails. CPU stays. Mon0 KO'd. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // Turn 2 (forced switch): Alice signals move 1 (Liquid). CPU evaluates Liquid damage. // Mon2(Nature) resists Liquid → takes less damage → picked. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 1, "", 0); + cpu.selectMove(battleKey, 1, uint104(0), 0); engine.resetCallContext(); activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 2, "CPU should switch to Nature (resists Liquid attack)"); @@ -649,11 +657,11 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Turn 0: lead selection - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU should use KO move. Alice attacks weakly. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // Alice's mon should be KO'd int32 aliceKO = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.IsKnockedOut); @@ -680,11 +688,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = moves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Both attack. CPU outspeeds → KOs Alice first. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 aliceKO = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.IsKnockedOut); assertEq(aliceKO, 1, "CPU should KO Alice when outspeeding"); @@ -716,11 +724,11 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Fire, Type.Liquid, 5); // 0.5x bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU outsped and opponent can KO → CPU should switch to Liquid. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should switch to Liquid when outsped in KO race"); @@ -755,11 +763,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU should pick the cheaper KO move (cost=1). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // Stamina delta should be -1 (cheap move used) int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -792,11 +800,11 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Fire, Type.Nature, 20); // 2x bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice switches to mon 1 (Nature, hp=20). CPU should KO it. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); engine.resetCallContext(); // Alice's mon 1 should be KO'd int32 aliceMon1KO = engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.IsKnockedOut); @@ -837,11 +845,11 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Liquid, Type.Nature, 5); // 0.5x bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice switches to Nature mon. CPU should use Fire attack (best damage). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); engine.resetCallContext(); // Alice's Nature mon should take Fire damage (500) int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.Hp); @@ -873,11 +881,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice switches. CPU has no affordable moves → rests. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); engine.resetCallContext(); // CPU stamina should be unchanged (rested) int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -915,11 +923,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice rests. No KO possible (hp=500). CPU should use strongest move in P4. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDelta, -400, "CPU should use bp=80 move for 400 damage"); @@ -949,11 +957,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice rests. CPU has no affordable moves → also rests. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); assertEq(staminaDelta, 0, "CPU should rest when no affordable moves"); @@ -994,14 +1002,14 @@ contract BetterCPUTest is Test { bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); // Lead selection: Alice is Liquid type. Liquid→Metal=1x, Liquid→Liquid=1x. Neutral. Mon0 leads. - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should lead with Mon0 (Metal)"); // Turn 1: Alice uses Fire attack. Lethal to Metal. CPU switches to Liquid. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should switch to Liquid to survive lethal Fire attack"); @@ -1027,11 +1035,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = moves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice attacks weakly. CPU stays and attacks back. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should stay when damage is low"); @@ -1056,10 +1064,13 @@ contract BetterCPUTest is Test { uint256[] memory moves = new uint256[](1); moves[0] = uint256(uint160(address(strongAttack))); + // CPU mons are faster so they attack first and get to deal damage before being KO'd. Mon[] memory cpuTeam = new Mon[](2); cpuTeam[0] = _createMon(Type.Fire, 100, 50, 10); + cpuTeam[0].stats.speed = 100; cpuTeam[0].moves = moves; cpuTeam[1] = _createMon(Type.Fire, 100, 50, 10); + cpuTeam[1].stats.speed = 100; cpuTeam[1].moves = moves; Mon[] memory aliceTeam = new Mon[](2); @@ -1069,11 +1080,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = moves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Both lethal, no material improvement → CPU stays and attacks. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // CPU should have stayed (attacked, not switched) int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); @@ -1111,14 +1122,14 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Fire, Type.Liquid, 5); // 0.5x bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should lead with Mon0"); // Turn 1: Alice Fire attack → lethal to Metal, Liquid survives → switch. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should switch to Liquid (materially better)"); @@ -1148,11 +1159,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice uses Self move. CPU skips P5, attacks in P6. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 0, "CPU should stay when opponent uses Self move"); @@ -1196,11 +1207,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice attacks weakly. CPU uses best move in P6. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDelta, -400, "CPU should use bp=80 for 400 damage"); @@ -1237,11 +1248,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU should pick cheaper move (cost=1). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); assertEq(staminaDelta, -1, "CPU should pick cheaper move within damage threshold"); @@ -1278,11 +1289,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU should pick bp=100 (cost=3) since bp=50 is outside threshold. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); assertEq(staminaDelta, -3, "CPU should pick strongest move when cheap one is outside threshold"); @@ -1314,11 +1325,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU can't afford moves → rests. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); assertEq(staminaDelta, 0, "CPU should rest when no affordable moves"); @@ -1349,11 +1360,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU exhausted, switches to Mon1. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should switch when exhausted and switch available"); @@ -1397,12 +1408,12 @@ contract BetterCPUTest is Test { // Set preferred move: monIndex=0, key=CONFIG_PREFERRED_MOVE(0), value=2 (moveIndex 1 + 1) cpu.setMonConfig(0, 0, 2); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU should use preferred move (bp=90). Damage = 90*50/10 = 450. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDelta, -450, "CPU should use preferred move (bp=90) within threshold"); @@ -1444,12 +1455,12 @@ contract BetterCPUTest is Test { // Set preferred move: monIndex=0, key=CONFIG_PREFERRED_MOVE(0), value=2 (moveIndex 1 + 1) cpu.setMonConfig(0, 0, 2); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Preferred too weak → CPU uses bp=100. Damage = 100*50/10 = 500. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDelta, -500, "CPU should ignore preferred move when too weak"); @@ -1491,19 +1502,19 @@ contract BetterCPUTest is Test { // Set switch-in move: monIndex=0, key=CONFIG_SWITCH_IN_MOVE(1), value=2 (moveIndex 1 + 1) cpu.setMonConfig(0, 1, 2); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice rests (P4 safe turn). CPU uses switch-in move (Self, bp=0). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDeltaTurn1 = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDeltaTurn1, 0, "Turn 1: CPU should use Self switch-in move (no damage)"); // Turn 2: Alice rests again. Switch-in move already used → normal P4 (best damage). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDeltaTurn2 = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDeltaTurn2, -250, "Turn 2: CPU should use attack move (damage 250)"); @@ -1545,19 +1556,19 @@ contract BetterCPUTest is Test { // Set switch-in move for Mon0 cpu.setMonConfig(0, 1, 2); // moveIndex 1 + 1 - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Alice rests. CPU uses switch-in Self move. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); int32 aliceHpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); assertEq(aliceHpDelta, 0, "Turn 1: switch-in Self move fires (no damage)"); // Turn 2: Alice rests. CPU attacks normally (switch-in already used). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Turn 3: Alice switches to mon 1. CPU re-evaluates. // On the switch turn, the CPU gets the switch-in move bit cleared for Mon0 when switching. @@ -1597,11 +1608,11 @@ contract BetterCPUTest is Test { typeCalc.setTypeEffectiveness(Type.Fire, Type.Liquid, 5); // 0.5x so Liquid is a viable switch bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: Both can KO. Speed tie → _weGoFirst returns false → CPU should switch. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); assertEq(activeIndex[1], 1, "CPU should switch on speed tie (play it safe)"); @@ -1631,11 +1642,11 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU priority 5 > Alice priority 1 → CPU goes first, KOs Alice. mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 aliceKO = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.IsKnockedOut); assertEq(aliceKO, 1, "CPU should KO Alice with higher priority move"); @@ -1670,7 +1681,7 @@ contract BetterCPUTest is Test { aliceTeam[1].moves = aliceMoves; bytes32 battleKey = _startBattleWithCPU(aliceTeam, cpuTeam); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1: CPU can't afford moves. Has switch option but P5 won't trigger (10 damage = 5%). // Falls through to P6: no moves. Switch available → might switch. @@ -1680,7 +1691,7 @@ contract BetterCPUTest is Test { // To force no-op, make team size 1? Can't, validator requires >= 2 for MONS_PER_TEAM. // Alternative: test that stamina is unchanged (CPU didn't attack). mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); assertEq(staminaDelta, 0, "CPU stamina should be unchanged (couldn't afford attack)"); diff --git a/test/CPUTest.sol b/test/CPUTest.sol index 6cfaffd3..3b20acba 100644 --- a/test/CPUTest.sol +++ b/test/CPUTest.sol @@ -217,7 +217,7 @@ contract CPUTest is Test { // Alice selects mon 2, CPU selects mon 1 mockCPURNG.setRNG(1); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(2)); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(2)); engine.resetCallContext(); // Assert active mon index for both p0 and p1 are correct assertEq(engine.getActiveMonIndexForBattleState(battleKey)[0], 2); @@ -232,7 +232,7 @@ contract CPUTest is Test { // Alice KO's the CPU's mon, the CPU chooses no op mockCPURNG.setRNG(0); // [no op, move 1, move 2, swap 0, swap 2, swap 3] and we want no op at index 0 - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); engine.resetCallContext(); // Check that the CPU now has 3 moves, all of which are switching to mon index 0, 2, or 3 { @@ -250,7 +250,7 @@ contract CPUTest is Test { } // Alice chooses no op (choice is irrelevant here), CPU chooses to switch to mon index 0 - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert the CPU now has mon index 0 as the active mon assertEq(engine.getActiveMonIndexForBattleState(battleKey)[1], 0); @@ -266,7 +266,7 @@ contract CPUTest is Test { mockCPURNG.setRNG(2); // [no op, move 1, move 2, swap 2, swap 3] and we want move 2 at index 2 // (note that the swaps are 0-indexed, and the moves are 1-indexed to refer to the above variable // naming convention, sorry D: ) - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert that there are now 3 moves, switching to mon index 2, 3, and no op (all stamina has been consumed) { @@ -277,7 +277,7 @@ contract CPUTest is Test { // Alice chooses no op, CPU chooses swapping to mon index 3 mockCPURNG.setRNG(2); // [no op, swap 2, swap 3] and we want swap 3 at index 2 - cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert the CPU now has mon index 3 as the active mon assertEq(engine.getActiveMonIndexForBattleState(battleKey)[1], 3); @@ -370,15 +370,15 @@ contract CPUTest is Test { // Verify that calculateMove returns the correct move CPUContext memory cpuCtx = engine.getCPUContext(battleKey); - (uint128 moveIndex, uint240 extraData) = playerCPU.calculateMove(cpuCtx, 0, 0); + (uint128 moveIndex, uint16 extraData) = playerCPU.calculateMove(cpuCtx, 0, 0); assertEq(moveIndex, 0); assertEq(extraData, 0); // Execute the turn - playerCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1)); + playerCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1)); engine.resetCallContext(); // Second turn: p0 sets move 1 for PlayerCPU (should override previous move) - playerCPU.setMove(battleKey, 1, uint240(42)); + playerCPU.setMove(battleKey, 1, uint16(42)); // Verify that calculateMove now returns the new move cpuCtx = engine.getCPUContext(battleKey); @@ -387,7 +387,7 @@ contract CPUTest is Test { assertEq(uint256(extraData), 42); // Execute another turn to verify the flow continues to work - playerCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + playerCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); } @@ -455,7 +455,7 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Player switches in mon index 0 (Fire type) - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Get active index for battle, it should be the resisted mon uint256[] memory activeIndex = engine.getActiveMonIndexForBattleState(battleKey); @@ -506,11 +506,11 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Turn 0, both player send in mon index 0 - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1, player rests, CPU should select no op because the move costs too much stamina mockCPURNG.setRNG(1); - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); } @@ -559,17 +559,17 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Turn 0, both player send in mon index 0 - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1, player rests, CPU should select move index 0 mockCPURNG.setRNG(1); // This triggers the OkayCPU to select a move, which should set its stamina delta to be -3 - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert the stamina delta for P1's active mon is -3 assertEq(engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina), -3); // Turn 2, player rests, CPU should rest as well - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert the stamina delta for P1's active mon is still -3 (it didn't go down more) assertEq(engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina), -3); @@ -621,10 +621,10 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Turn 0, both player send in mon index 0 - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1, p0 rests, CPU should select move index 1 (self move) - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert that the stamina delta is -1 for p1's active mon int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -678,10 +678,10 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Turn 0, both player send in mon index 0 - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1, p0 rests, CPU should select move index 1 (self move) - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert that the stamina delta is -1 for p1's active mon int32 staminaDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina); @@ -735,14 +735,14 @@ contract CPUTest is Test { bytes32 battleKey = okayCPU.startBattle(proposal); // Turn 0, both player send in mon index 0 - okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0)); + okayCPU.selectMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0)); engine.resetCallContext(); // Turn 1, set RNG to trigger smart random select and pick move index 1 // RNG needs: (RNG % 6 == 5) to trigger smart random, (RNG % 3 != 0) to not switch, ((RNG >> 8) % 2 == 1) to pick move 1 // 257 satisfies all: 257 % 6 = 5, 257 % 3 = 2, (257 >> 8) = 1 // So both mons should take 1 damage, as p0 also selects the damage move mockCPURNG.setRNG(257); - okayCPU.selectMove(battleKey, 1, "", 0); + okayCPU.selectMove(battleKey, 1, uint104(0), 0); engine.resetCallContext(); // Assert that the hp delta is -1 for p0's active mon and p1's active mon int32 hpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); @@ -753,7 +753,7 @@ contract CPUTest is Test { // Turn 2, set RNG to be 0 (do not trigger short circuit) // CPU should select no-op because no type advantage is currently set mockCPURNG.setRNG(0); - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert that the hp delta is still -1 for p0's active mon hpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); @@ -763,7 +763,7 @@ contract CPUTest is Test { typeCalc.setTypeEffectiveness(Type.Liquid, Type.Liquid, 2); // Now the CPU should select the damage move (move index 1) because it has a type advantage - okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, "", 0); + okayCPU.selectMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0); engine.resetCallContext(); // Assert that the hp delta is -2 for p0's active mon hpDelta = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Hp); diff --git a/test/DefaultCommitManagerTest.sol b/test/DefaultCommitManagerTest.sol index 8ba783fe..b3da8de8 100644 --- a/test/DefaultCommitManagerTest.sol +++ b/test/DefaultCommitManagerTest.sol @@ -85,19 +85,19 @@ contract DefaultCommitManagerTest is Test, BattleHelper { // Alice commits vm.startPrank(ALICE); uint8 moveIndex = SWITCH_MOVE_INDEX; - bytes32 moveHash = keccak256(abi.encodePacked(moveIndex, bytes32(""), uint240(0))); + bytes32 moveHash = keccak256(abi.encodePacked(moveIndex, uint104(0), uint16(0))); commitManager.commitMove(battleKey, moveHash); // Alice tries to reveal vm.expectRevert(DefaultCommitManager.NotYetRevealed.selector); - commitManager.revealMove(battleKey, moveIndex, bytes32(""), uint240(0), false); + commitManager.revealMove(battleKey, moveIndex, uint104(0), uint16(0), false); } function test_RevealBeforeSelfCommit() public { bytes32 battleKey = _startBattle(validator, engine, defaultOracle, defaultRegistry, matchmaker, address(commitManager)); // Alice sets commitment _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob sets commitment _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, 0, 0); @@ -108,13 +108,13 @@ contract DefaultCommitManagerTest is Test, BattleHelper { // Alice's turn again to move vm.startPrank(ALICE); vm.expectRevert(DefaultCommitManager.RevealBeforeSelfCommit.selector); - commitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, bytes32(""), 0, false); + commitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0, false); } function test_BattleNotYetStarted() public { vm.startPrank(ALICE); vm.expectRevert(DefaultCommitManager.BattleNotYetStarted.selector); - commitManager.revealMove(bytes32(0), NO_OP_MOVE_INDEX, bytes32(""), 0, false); + commitManager.revealMove(bytes32(0), NO_OP_MOVE_INDEX, uint104(0), 0, false); vm.startPrank(BOB); vm.expectRevert(DefaultCommitManager.BattleNotYetStarted.selector); commitManager.commitMove(bytes32(0), bytes32(0)); @@ -127,7 +127,7 @@ contract DefaultCommitManagerTest is Test, BattleHelper { engine.end(battleKey); vm.startPrank(ALICE); vm.expectRevert(DefaultCommitManager.BattleAlreadyComplete.selector); - commitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, bytes32(""), 0, false); + commitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0, false); vm.startPrank(BOB); vm.expectRevert(DefaultCommitManager.BattleAlreadyComplete.selector); commitManager.commitMove(battleKey, bytes32(0)); @@ -157,7 +157,7 @@ contract DefaultCommitManagerTest is Test, BattleHelper { vm.startPrank(ALICE); commitManager.commitMove(battleKey, bytes32("1")); vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(""), uint240(0), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(0), false); vm.warp(TIMEOUT * validator.PREV_TURN_MULTIPLIER() + 1); engine.end(battleKey); assertEq(engine.getWinner(battleKey), BOB); diff --git a/test/EngineGasTest.sol b/test/EngineGasTest.sol index c80bd59a..a60e285c 100644 --- a/test/EngineGasTest.sol +++ b/test/EngineGasTest.sol @@ -49,9 +49,14 @@ contract EngineGasTest is Test, BattleHelper { TestTeamRegistry defaultRegistry; DefaultMatchmaker matchmaker; - // Helper to pack StatBoostsMove extraData: lower 60 bits = playerIndex, next 60 bits = monIndex, next 60 bits = statIndex, upper 60 bits = boostAmount - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint240) { - return uint240(playerIndex | (monIndex << 60) | (statIndex << 120) | (uint256(uint32(boostAmount)) << 180)); + // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] + function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { + return uint16( + (playerIndex & 0x1) + | ((monIndex & 0x7) << 1) + | ((statIndex & 0xF) << 4) + | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) + ); } function setUp() public { @@ -142,16 +147,16 @@ contract EngineGasTest is Test, BattleHelper { // - Alice swaps in mon index 3 // - Alice rests, Bob KOs vm.startSnapshotGas("FirstBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Alice uses burn, Bob uses frostbite _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 1, 0, 0); // Bob is mon index 0, we boost attack by 90% - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); // Alice is now mon index 1, Bob is mon index 0 _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); // Alice swaps in mon index 0 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0), true); engine.resetCallContext(); // Alice is now mon index 0, Bob rests _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); @@ -159,7 +164,7 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 3, NO_OP_MOVE_INDEX, 0, 0); // Bob sends in mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice rests, Bob uses self-stat boost _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); @@ -167,13 +172,13 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); // Alice swaps in mon index 2 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); // Alice rests, Bob KOs _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); // Alice swaps in mon index 3 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); // Alice rests, Bob KOs _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); @@ -223,24 +228,24 @@ contract EngineGasTest is Test, BattleHelper { // - Both players send in mon 0 vm.startSnapshotGas("SecondBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // - Alice sets up self-stat boost (move 3), Bob sets up Burn (move 1) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, 1, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); // - Alice KOs Bob (move 0 = damage) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); // - Bob swaps in mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // - Alice swaps in mon index 1, Bob sets up Frostbite (move 2) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, 2, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, 2, uint16(1), 0); // - Alice sets up self-stat boost (move 3, playerIndex=0, monIndex=1), Bob rests _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, NO_OP_MOVE_INDEX, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); // - Alice KOs Bob (move 0) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); // - Bob sends in mon index 2 vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); // - Alice rests, Bob uses self-stat boost (move 3, playerIndex=1, monIndex=2) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, NO_OP_MOVE_INDEX, 3, 0, _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); @@ -248,7 +253,7 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, NO_OP_MOVE_INDEX, 0, 0, 0); // - Alice swaps in mon index 2 vm.startPrank(ALICE); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); // - Alice uses self-stat boost (move 3, p0 mon2), Bob uses self-stat boost (move 3, p1 mon2) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, 3, _packStatBoost(0, 2, uint256(MonStateIndexName.Attack), int32(90)), _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); @@ -256,7 +261,7 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); // - Bob sends in mon index 3 vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); // - Alice KOs Bob (move 0) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); @@ -283,16 +288,16 @@ contract EngineGasTest is Test, BattleHelper { // Battle 3: Exact same sequence as Battle 1 vm.startSnapshotGas("ThirdBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Alice uses burn, Bob uses frostbite _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 0, 1, 0, 0); // Bob is mon index 0, we boost attack by 90% - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); // Alice is now mon index 1, Bob is mon index 0 _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); // Alice swaps in mon index 0 vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(0), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(0), true); engine.resetCallContext(); // Alice is now mon index 0, Bob rests _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); @@ -300,7 +305,7 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 3, NO_OP_MOVE_INDEX, 0, 0); // Bob sends in mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice rests, Bob uses self-stat boost _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); @@ -308,13 +313,13 @@ contract EngineGasTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); // Alice swaps in mon index 2 vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); // Alice rests, Bob KOs _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); // Alice swaps in mon index 3 vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); // Alice rests, Bob KOs _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); @@ -390,7 +395,7 @@ contract EngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Battle1_Execute"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); // Both switch in mon 0 + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Both switch in mon 0 _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey1, 0, 0, 0, 0); // Both attack - one dies // After this, battle should end uint256 execute1 = vm.stopSnapshotGas("Battle1_Execute"); @@ -404,7 +409,7 @@ contract EngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Battle2_Execute"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); // Both switch in mon 0 + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Both switch in mon 0 _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, 0, 0, 0); // Both attack - one dies uint256 execute2 = vm.stopSnapshotGas("Battle2_Execute"); @@ -469,7 +474,7 @@ contract EngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("B1_Execute"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Check after switch (BattleConfigView memory cfgAfterSwitch,) = engine.getBattle(battleKey1); @@ -510,7 +515,7 @@ contract EngineGasTest is Test, BattleHelper { console.log("After B2 setup - packedP1EffectsCount:", cfg2.packedP1EffectsCount); vm.startSnapshotGas("B2_Execute"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Both apply effect to each other (adds 2 effects - should REUSE slots) _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, 0, 0, 0); // Both attack - KO @@ -582,7 +587,7 @@ contract EngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("External_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, externalBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, externalBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, externalBattleKey, 0, 0, 0, 0); uint256 externalExecute = vm.stopSnapshotGas("External_Execute"); @@ -603,7 +608,7 @@ contract EngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Inline_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, inlineBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, inlineBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, inlineBattleKey, 0, 0, 0, 0); uint256 inlineExecute = vm.stopSnapshotGas("Inline_Execute"); @@ -664,7 +669,7 @@ contract EngineGasTest is Test, BattleHelper { new IEngineHook[](0), simpleRuleset, address(commitManager) ); vm.warp(vm.getBlockTimestamp() + 1); - _commitRevealExecuteForEngine(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(engine, commitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(engine, commitManager, battleKey1, 0, 0, 0, 0); // Get final HP deltas @@ -684,7 +689,7 @@ contract EngineGasTest is Test, BattleHelper { new IEngineHook[](0), inlineRuleset, address(inlineCM) ); vm.warp(vm.getBlockTimestamp() + 1); - _commitRevealExecuteForEngine(inlineEngine, inlineCM, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCM, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCM, battleKey2, 0, 0, 0, 0); // Get final HP deltas @@ -756,10 +761,10 @@ contract EngineGasTest is Test, BattleHelper { bytes32 battleKey, uint8 aliceMoveIndex, uint8 bobMoveIndex, - uint240 aliceExtraData, - uint240 bobExtraData + uint16 aliceExtraData, + uint16 bobExtraData ) internal { - bytes32 salt = ""; + uint104 salt = 0; bytes32 aliceMoveHash = keccak256(abi.encodePacked(aliceMoveIndex, salt, aliceExtraData)); bytes32 bobMoveHash = keccak256(abi.encodePacked(bobMoveIndex, salt, bobExtraData)); uint256 turnId = eng.getTurnIdForBattleState(battleKey); diff --git a/test/EngineGlobalKVTest.sol b/test/EngineGlobalKVTest.sol index f3d42257..7b1f7990 100644 --- a/test/EngineGlobalKVTest.sol +++ b/test/EngineGlobalKVTest.sol @@ -30,12 +30,12 @@ contract EngineGlobalKVTest is Test, BattleHelper { DefaultValidator validator; // Arbitrary keys used throughout the tests. - uint240 constant KEY_A = 1001; - uint240 constant KEY_B = 1002; - uint240 constant KEY_C = 1003; - uint240 constant KEY_D = 1004; - uint240 constant KEY_E = 1005; - uint240 constant KEY_F = 1006; + uint16 constant KEY_A = 1001; + uint16 constant KEY_B = 1002; + uint16 constant KEY_C = 1003; + uint16 constant KEY_D = 1004; + uint16 constant KEY_E = 1005; + uint16 constant KEY_F = 1006; function setUp() public { mockOracle = new MockRandomnessOracle(); @@ -71,7 +71,7 @@ contract EngineGlobalKVTest is Test, BattleHelper { defaultRegistry.setTeam(BOB, team); battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); } @@ -82,22 +82,22 @@ contract EngineGlobalKVTest is Test, BattleHelper { defaultRegistry.setTeam(BOB, team); battleKey2 = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); } /// @dev Both players use the mock write-move with their respective keys. - function _bothWrite(bytes32 battleKey, uint240 aliceKey, uint240 bobKey) internal { + function _bothWrite(bytes32 battleKey, uint16 aliceKey, uint16 bobKey) internal { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 0, aliceKey, bobKey); } /// @dev Alice writes; Bob rests. - function _aliceWrites(bytes32 battleKey, uint240 aliceKey) internal { + function _aliceWrites(bytes32 battleKey, uint16 aliceKey) internal { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, aliceKey, 0); } /// @dev Bob writes; Alice rests. - function _bobWrites(bytes32 battleKey, uint240 bobKey) internal { + function _bobWrites(bytes32 battleKey, uint16 bobKey) internal { _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 0, 0, bobKey); } @@ -127,7 +127,8 @@ contract EngineGlobalKVTest is Test, BattleHelper { bytes32 firstPacked = view1.globalKVEntries[0].value; // Second call re-writes via encoded value bump so we can confirm it changed. - uint240 extraData = KEY_A | (uint240(42) << 64); + // Layout: bits 0..9 = key, bits 10..15 = value. + uint16 extraData = KEY_A | (uint16(42) << 10); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, extraData, 0); (BattleConfigView memory view2,) = engine.getBattle(battleKey); diff --git a/test/EngineOptimizationTest.sol b/test/EngineOptimizationTest.sol index 943bdb22..607f235a 100644 --- a/test/EngineOptimizationTest.sol +++ b/test/EngineOptimizationTest.sol @@ -116,7 +116,7 @@ contract EngineOptimizationTest is Test, BattleHelper { // Switch in mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses effect move on Bob (Bob does NoOp) @@ -175,7 +175,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); @@ -248,7 +248,7 @@ contract EngineOptimizationTest is Test, BattleHelper { // Switch in mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both use 5-stamina move. After: -5 from move + 1 from RoundEnd regen = -4 @@ -318,7 +318,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both use 5-stamina move: staminaDelta = -5 + 1 (RoundEnd) = -4 @@ -393,7 +393,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Use 1-stamina move: -1 from move + 1 from RoundEnd regen = 0 (not +1) @@ -414,7 +414,7 @@ contract EngineOptimizationTest is Test, BattleHelper { _forceP1Switch(testEngine, signedManager, battleKey); - _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint240(1)); + _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint16(1)); uint256[] memory activeMons = testEngine.getActiveMonIndexForBattleState(battleKey); assertEq(activeMons[1], 1, "P1 should switch to mon 1"); @@ -433,7 +433,7 @@ contract EngineOptimizationTest is Test, BattleHelper { vm.startPrank(ALICE); vm.expectRevert(DefaultCommitManager.PlayerNotAllowed.selector); - signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(1)); + signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); vm.stopPrank(); } @@ -442,12 +442,12 @@ contract EngineOptimizationTest is Test, BattleHelper { _startSignedInlineSwitchBattle(false); _commitRevealExecuteForAliceAndBob( - testEngine, signedManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + testEngine, signedManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); vm.startPrank(BOB); vm.expectRevert(SignedCommitManager.NotSinglePlayerTurn.selector); - signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(1)); + signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); vm.stopPrank(); } @@ -456,11 +456,11 @@ contract EngineOptimizationTest is Test, BattleHelper { _startSignedInlineSwitchBattle(false); _forceP1Switch(testEngine, signedManager, battleKey); - _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint240(1)); + _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint16(1)); uint256 turnBefore = testEngine.getTurnIdForBattleState(battleKey); _commitRevealExecuteForAliceAndBob( - testEngine, signedManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + testEngine, signedManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); assertEq(testEngine.getTurnIdForBattleState(battleKey), turnBefore + 1, "Normal fallback should execute"); @@ -473,7 +473,7 @@ contract EngineOptimizationTest is Test, BattleHelper { _forceP1Switch(testEngine, signedManager, battleKey); vm.prank(BOB); - signedManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(1), true); + signedManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), true); testEngine.resetCallContext(); uint256[] memory activeMons = testEngine.getActiveMonIndexForBattleState(battleKey); @@ -489,11 +489,11 @@ contract EngineOptimizationTest is Test, BattleHelper { (Engine testEngine, SignedCommitManager signedManager, bytes32 battleKey) = _startSignedInlineSwitchBattle(true); _forceP1Switch(testEngine, signedManager, battleKey); - _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint240(1)); + _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, BOB, uint16(1)); assertEq(testEngine.getPlayerSwitchForTurnFlagForBattleState(battleKey), 0, "P0 should be forced to switch"); - _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, ALICE, uint240(1)); + _executeSinglePlayerMoveAndReset(testEngine, signedManager, battleKey, ALICE, uint16(1)); assertEq( testEngine.getPlayerSwitchForTurnFlagForBattleState(battleKey), 2, @@ -502,7 +502,7 @@ contract EngineOptimizationTest is Test, BattleHelper { uint256 turnBefore = testEngine.getTurnIdForBattleState(battleKey); _commitRevealExecuteForAliceAndBob( - testEngine, signedManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + testEngine, signedManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); assertEq( @@ -514,10 +514,10 @@ contract EngineOptimizationTest is Test, BattleHelper { function _forceP1Switch(Engine testEngine, SignedCommitManager signedManager, bytes32 battleKey) internal { _commitRevealExecuteForAliceAndBob( - testEngine, signedManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + testEngine, signedManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob( - testEngine, signedManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + testEngine, signedManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); assertEq(testEngine.getPlayerSwitchForTurnFlagForBattleState(battleKey), 1, "P1 should be forced to switch"); @@ -528,10 +528,10 @@ contract EngineOptimizationTest is Test, BattleHelper { SignedCommitManager signedManager, bytes32 battleKey, address player, - uint240 monIndex + uint16 monIndex ) internal { vm.prank(player); - signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), monIndex); + signedManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), monIndex); testEngine.resetCallContext(); } @@ -665,7 +665,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, warmupKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, warmupKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob(engine, commitManager, warmupKey, 0, 0, 0, 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, warmupKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, 0, 0); @@ -683,7 +683,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, externalKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, externalKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); vm.startSnapshotGas("ExternalStaminaRegen"); @@ -704,7 +704,7 @@ contract EngineOptimizationTest is Test, BattleHelper { ); vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, inlineKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, inlineKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); vm.startSnapshotGas("InlineStaminaRegen"); diff --git a/test/EngineTest.sol b/test/EngineTest.sol index 3579cdfa..7ff48a80 100644 --- a/test/EngineTest.sol +++ b/test/EngineTest.sol @@ -62,9 +62,9 @@ contract EngineTest is Test, BattleHelper { Mon dummyMon; IMoveSet dummyAttack; - // Helper to pack ForceSwitchMove extraData: lower 120 bits = playerIndex, upper 120 bits = monToSwitchIndex - function _packForceSwitch(uint256 playerIndex, uint256 monToSwitchIndex) internal pure returns (uint240) { - return uint240(playerIndex | (monToSwitchIndex << 120)); + // Pack ForceSwitchMove extraData: bit 0 = playerIndex (0/1), bits 1..15 = monToSwitchIndex + function _packForceSwitch(uint256 playerIndex, uint256 monToSwitchIndex) internal pure returns (uint16) { + return uint16((playerIndex & 0x1) | (monToSwitchIndex << 1)); } function setUp() public { @@ -123,7 +123,7 @@ contract EngineTest is Test, BattleHelper { // Let Alice and Bob both commit to switching _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob do a no-op @@ -190,7 +190,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -266,7 +266,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -348,7 +348,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -374,7 +374,7 @@ contract EngineTest is Test, BattleHelper { // Reveal Alice's move, and advance game state vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(""), uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), false); engine.execute(battleKey); engine.resetCallContext(); @@ -400,7 +400,7 @@ contract EngineTest is Test, BattleHelper { // Attempt to reveal Alice's move, and assert that we cannot advance the game state vm.startPrank(ALICE); vm.expectRevert(abi.encodeWithSignature("InvalidMove(address)", ALICE)); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(""), uint240(0), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(0), false); // Attempt to forcibly advance the game state vm.expectRevert(); @@ -476,7 +476,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -534,11 +534,11 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Second move, have Alice swap out to mon at index 1, have Bob use attack - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Assert that mon index for Alice is 1 // Assert that the mon state for Alice has -5 applied to the switched in mon @@ -587,11 +587,11 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Second move, have Alice swap out to mon at index 1, have Bob use fast attack - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Assert that mon index for Alice is 1 // Assert that the mon state for Alice has -5 applied to the previous mon @@ -640,11 +640,11 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Second move, have Alice swap out to mon at index 1, have Bob use fast attack which supersedes Switch - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Given that it's a KO (even though Alice chose switch), // check that now they have the priority flag again @@ -699,7 +699,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -752,7 +752,7 @@ contract EngineTest is Test, BattleHelper { // Select mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both attack (costs 1 stamina each) @@ -829,7 +829,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -900,19 +900,19 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, defaultOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Commit move index 0 for Bob uint8 moveIndex = 0; vm.startPrank(BOB); - bytes32 bobMoveHash = keccak256(abi.encodePacked(moveIndex, bytes32(""), uint240(0))); + bytes32 bobMoveHash = keccak256(abi.encodePacked(moveIndex, uint104(0), uint16(0))); commitManager.commitMove(battleKey, bobMoveHash); // Assert that Alice cannot reveal anything because of the stamina cost (she has the high stamina cost mon) vm.startPrank(ALICE); vm.expectRevert(abi.encodeWithSignature("InvalidMove(address)", ALICE)); - commitManager.revealMove(battleKey, moveIndex, bytes32(""), uint240(0), false); + commitManager.revealMove(battleKey, moveIndex, uint104(0), uint16(0), false); } // Ensure that we cannot write to mon state when there is no active execute() call in the call stack @@ -997,13 +997,13 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, which for Bob afflicts the instant death condition on the // opposing mon (Alice's) and knocks it out uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Assert Bob wins @@ -1076,20 +1076,20 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, which for Bob afflicts the instant death condition on the // opposing mon (Alice's) and knocks it out uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Now only Alice should be able to switch vm.startPrank(ALICE); // Alice should be able to reveal because she is the only player (player flag should be set) - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(""), uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), false); // Execute the switch engine.execute(battleKey); @@ -1135,13 +1135,13 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice swaps to mon index 1, and Bob applies the effect // The effect should be applied to mon index 1 for Alice but only during the duration of the turn // (We have a check for 2 instead of 1 to avoid confusing it with the base case state) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Assert that the temporary stat boost effect is updated to 2 because the roundEnd hook also runs assertEq(engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.Attack), 2); @@ -1209,14 +1209,14 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, which for Bob afflicts the instant death condition on the // opposing mon (Alice's) // But Alice's mon should KO Bob's before the end of round takes place uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Assert Alice wins @@ -1289,26 +1289,26 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, which for Bob afflicts the instant death condition on the // opposing mon (Alice's) and knocks it out // But Bob moves first (higher priority), so he gets the instant death affliction uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Now if Alice tries to pick a non-switch move, the engine should revert vm.startPrank(ALICE); - bytes32 salt = ""; + uint104 salt = 0; uint8 aliceMoveIndex = 0; bytes32 aliceMoveHash = keccak256(abi.encodePacked(aliceMoveIndex, salt, extraData)); commitManager.commitMove(battleKey, aliceMoveHash); // Bob reveals a swap vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint16(1), false); // Alice's reveal will revert (must choose switch) vm.startPrank(ALICE); @@ -1382,19 +1382,19 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, which for Bob afflicts the instant death condition on the // opposing mon (Alice's) and knocks it out // But Bob moves first (higher priority), so he gets the instant death affliction uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Now both moves have to swap to index 1 for their mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(1), uint240(1) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(1), uint16(1) ); } @@ -1456,7 +1456,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0 @@ -1464,7 +1464,7 @@ contract EngineTest is Test, BattleHelper { // Alice tries to go fast for a lethal effect // Bob should win priority and inflict skip turn effect uint8 moveIndex = 0; - uint240 extraData = 0; + uint16 extraData = 0; _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, moveIndex, moveIndex, extraData, extraData); // Assert no winner, and no damage dealt @@ -1538,7 +1538,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, but Alice encodes a swap to mon index 1 for player index 1 (Bob) @@ -1617,7 +1617,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both player pick move index 0, but Alice encodes a swap to mon index 1 for player index 0 (Alice) @@ -1696,12 +1696,12 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Bob commit and reveal to attack (move index 0) - uint240 extraData = 0; - bytes32 salt = ""; + uint16 extraData = 0; + uint104 salt = 0; uint8 moveIndex = 0; vm.startPrank(BOB); commitManager.commitMove(battleKey, keccak256(abi.encodePacked(moveIndex, salt, extraData))); @@ -1788,12 +1788,12 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Bob commit and reveal to attack (move index 0) - uint240 extraData = 0; - bytes32 salt = ""; + uint16 extraData = 0; + uint104 salt = 0; uint8 moveIndex = 0; vm.startPrank(BOB); commitManager.commitMove(battleKey, keccak256(abi.encodePacked(moveIndex, salt, extraData))); @@ -1885,7 +1885,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let both players select move index 0 @@ -1955,7 +1955,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // (Have Alice force Bob to switch to mon index 1, have Bob select the instant death switch in effect @@ -2024,12 +2024,12 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Have Alice switch to their second mon, have Bob select the instant death switch in effect // (But swapping to mon index 1 for Alice will trigger on switch in and kill the mon) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Assert that the player switch for turn flag is now 0, indicating Alice has to switch (, BattleData memory state) = engine.getBattle(battleKey); @@ -2099,7 +2099,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // After this, Alice's mon should be dead and Bob should be the winner @@ -2193,7 +2193,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice switches themselves to mon index 1, while Bob chooses move index 0 @@ -2293,11 +2293,11 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice switches themselves to mon index 1, while Bob chooses move index 0 - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Assert that Alice's new mon is now KOed assertEq(engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.IsKnockedOut), 1); @@ -2349,7 +2349,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks (they should apply the single instance effect on hit) @@ -2395,19 +2395,19 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob commits to NO_OP vm.startPrank(BOB); uint8 moveIndex = NO_OP_MOVE_INDEX; - bytes32 bobMoveHash = keccak256(abi.encodePacked(moveIndex, bytes32(""), uint240(0))); + bytes32 bobMoveHash = keccak256(abi.encodePacked(moveIndex, uint104(0), uint16(0))); commitManager.commitMove(battleKey, bobMoveHash); // Alice should revert when revealing vm.startPrank(ALICE); vm.expectRevert(abi.encodeWithSignature("InvalidMove(address)", ALICE)); - commitManager.revealMove(battleKey, 0, bytes32(""), uint240(0), false); + commitManager.revealMove(battleKey, 0, uint104(0), uint16(0), false); } function test_onMonSwitchOutHookWorksWithTempStatBoost() public { @@ -2451,7 +2451,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks (they should apply the temporary stat boost effect) @@ -2463,7 +2463,7 @@ contract EngineTest is Test, BattleHelper { // Alice and Bob both switch to mon index 1 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(1), uint240(1) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(1), uint16(1) ); // Assert that the temporary stat boost effect was removed from both mons @@ -2517,7 +2517,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks, both of them are move index 0 (do damage rebound) @@ -2568,8 +2568,8 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startBattle(twoMoveValidator, engine, defaultOracle, defaultRegistry, matchmaker, address(commitManager)); // Alice commits to swapping in mon index 1 - bytes32 salt = ""; - bytes32 aliceMoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(1))); + uint104 salt = 0; + bytes32 aliceMoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(1))); vm.startPrank(ALICE); commitManager.commitMove(battleKey, aliceMoveHash); @@ -2579,19 +2579,19 @@ contract EngineTest is Test, BattleHelper { // Bob reveals vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint16(1), false); // Bob cannot reveal twice vm.expectRevert(DefaultCommitManager.AlreadyRevealed.selector); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint16(1), false); // Alice reveals but does not execute vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint16(1), false); // Second reveal should also fail vm.expectRevert(DefaultCommitManager.AlreadyRevealed.selector); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint240(1), false); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, uint16(1), false); } function test_cannotCommitToEndedBattle() public { @@ -2622,7 +2622,7 @@ contract EngineTest is Test, BattleHelper { // Both players send in mon index 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob does nothing (it's his turn to move, so he'll lose by timeout) @@ -2671,7 +2671,7 @@ contract EngineTest is Test, BattleHelper { // Alice commits to switch to mon index 0 vm.startPrank(ALICE); - commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint240(0)))); + commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(0), uint16(0)))); // Attempt to end the battle immediately (same block as start) // Bob hasn't committed and timeout is 0, so Bob loses, but game should revert @@ -2722,10 +2722,10 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey, uint8 aliceMoveIndex, uint8 bobMoveIndex, - uint240 aliceExtraData, - uint240 bobExtraData + uint16 aliceExtraData, + uint16 bobExtraData ) internal { - bytes32 salt = ""; + uint104 salt = 0; bytes32 aliceMoveHash = keccak256(abi.encodePacked(aliceMoveIndex, salt, aliceExtraData)); bytes32 bobMoveHash = keccak256(abi.encodePacked(bobMoveIndex, salt, bobExtraData)); // Decide which player commits @@ -2830,8 +2830,8 @@ contract EngineTest is Test, BattleHelper { */ function test_turn0DefaultCommitManagerValidPreimage() public { bytes32 battleKey = _startDummyBattleWithTwoMons(); - bytes32 salt = ""; - uint240 extraData = uint240(0); + uint104 salt = 0; + uint16 extraData = uint16(0); bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, extraData)); // Ensure Alice cannot reveal yet because Alice has not yet committed @@ -2889,8 +2889,8 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startDummyBattleWithTwoMons(); // Let Alice commit to choosing switch - bytes32 salt = ""; - uint240 extraData = uint240(0); + uint104 salt = 0; + uint16 extraData = uint16(0); bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, extraData)); // Let Alice commit the first move (switching in mon index 0) @@ -2910,7 +2910,7 @@ contract EngineTest is Test, BattleHelper { commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, extraData, true); engine.resetCallContext(); // New turn, both players swap to mon index 1 - extraData = uint240(1); + extraData = uint16(1); moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, extraData)); // Ensure Bob cannot reveal yet because Bob has not yet committed @@ -2936,7 +2936,7 @@ contract EngineTest is Test, BattleHelper { // Let Alice reveal their invalid move index of 0 vm.startPrank(ALICE); vm.expectRevert(abi.encodeWithSignature("InvalidMove(address)", ALICE)); - uint240 invalidExtraData = uint240(0); + uint16 invalidExtraData = uint16(0); commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, invalidExtraData, true); engine.resetCallContext(); // Now let Alice reveal a valid move @@ -2971,8 +2971,8 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startDummyBattleWithTwoMons(); // Let Alice commit to choosing switch - bytes32 salt = ""; - uint240 extraData = uint240(0); + uint104 salt = 0; + uint16 extraData = uint16(0); bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, extraData)); vm.startPrank(ALICE); commitManager.commitMove(battleKey, moveHash); @@ -2991,8 +2991,8 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startDummyBattleWithTwoMons(); // Let Alice commit to choosing switch - bytes32 salt = ""; - uint240 extraData = uint240(0); + uint104 salt = 0; + uint16 extraData = uint16(0); bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, extraData)); vm.startPrank(ALICE); commitManager.commitMove(battleKey, moveHash); @@ -3067,7 +3067,7 @@ contract EngineTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Let Alice and Bob commit and reveal to both choosing attack (move index 0) @@ -3111,9 +3111,9 @@ contract EngineTest is Test, BattleHelper { ); bytes32 battleKey = _startBattle(validatorToUse, engine, defaultOracle, defaultRegistry, matchmaker, address(commitManager)); // Alice sends in mon index 0, Bob sends in the fast mon - _commitRevealExecuteForAliceAndBob(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(1)); + _commitRevealExecuteForAliceAndBob(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(1)); // Both players pick move index 0 - _commitRevealExecuteForAliceAndBob(battleKey, 0, 0, uint240(1), uint240(0)); + _commitRevealExecuteForAliceAndBob(battleKey, 0, 0, uint16(1), uint16(0)); // Switch for turn flag should be 0, Bob's active mon index should now be 0 (, BattleData memory state) = engine.getBattle(battleKey); assertEq(state.playerSwitchForTurnFlag, 0); @@ -3139,15 +3139,15 @@ contract EngineTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validatorToUse, engine, defaultOracle, defaultRegistry, matchmaker, address(commitManager)); // Alice and Bob send in their first mon - _commitRevealExecuteForAliceAndBob(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); // Verify the dummy effect is applied to Alice's mon (EffectInstance[] memory effects, uint256[] memory indices) = engine.getEffects(battleKey, 1, 0); assertEq(effects.length, 1); // Alice uses the edit effect attack to change the extra data to 69 on Bob - // Pack extraData: lower 80 bits = targetIndex (1), next 80 bits = monIndex (0), upper 80 bits = effectIndex - uint240 editExtraData = uint240(1 | (0 << 80) | (indices[0] << 160)); + // Pack extraData: bits 0..1 = targetIndex (1), bits 2..5 = monIndex (0), bits 6..15 = effectIndex + uint16 editExtraData = uint16(uint256(1) | (uint256(0) << 2) | (uint256(indices[0]) << 6)); _commitRevealExecuteForAliceAndBob(battleKey, 0, NO_OP_MOVE_INDEX, editExtraData, 0); (effects, ) = engine.getEffects(battleKey, 1, 0); assertEq(effects[0].data, bytes32(uint256(69))); diff --git a/test/GachaTest.sol b/test/GachaTest.sol index ba3af067..f116e144 100644 --- a/test/GachaTest.sol +++ b/test/GachaTest.sol @@ -125,7 +125,7 @@ contract GachaTest is Test, BattleHelper { // Alice commits switching to mon index 0 vm.startPrank(ALICE); - commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint240(0)))); + commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint16(0)))); // Alice wins the battle (inactivity for Bob), we skip ahead mockRNG.setRNG(1); // No extra bonus for points @@ -197,7 +197,7 @@ contract GachaTest is Test, BattleHelper { // Alice commits switching to mon index 0 vm.startPrank(ALICE); - commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint240(0)))); + commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint16(0)))); // Alice wins the battle engine.end(battleKey); @@ -295,7 +295,7 @@ contract GachaTest is Test, BattleHelper { _startBattle(validator, engine, defaultOracle, defaultRegistry, matchmaker, hooks, address(commitManager)); vm.warp(vm.getBlockTimestamp() + 1); vm.startPrank(ALICE); - commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint240(0)))); + commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint16(0)))); vm.stopPrank(); mockRNG.setRNG(1); engine.end(battleKey); @@ -318,7 +318,7 @@ contract GachaTest is Test, BattleHelper { _startBattle(validator, engine, defaultOracle, defaultRegistry, matchmaker, hooks, address(commitManager)); vm.warp(vm.getBlockTimestamp() + 1); vm.startPrank(ALICE); - commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint240(0)))); + commitManager.commitMove(battleKey, keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(""), uint16(0)))); vm.stopPrank(); mockRNG.setRNG(1); engine.end(battleKey); diff --git a/test/InlineAbilityParityTest.sol b/test/InlineAbilityParityTest.sol index 7320fff7..0bbf6940 100644 --- a/test/InlineAbilityParityTest.sol +++ b/test/InlineAbilityParityTest.sol @@ -117,7 +117,7 @@ contract InlineAbilityParityTest is Test, BattleHelper { // Turn 0: both switch in _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); assertTrue(_hasEffect(battleKey, 0, 0, address(singletonAbility)), "p0 should have effect"); @@ -139,18 +139,18 @@ contract InlineAbilityParityTest is Test, BattleHelper { // Turn 0: switch in mon 0 _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); assertEq(_countEffect(battleKey, 0, 0, address(singletonAbility)), 1, "should have 1 effect"); // Switch to mon 1 then back to mon 0 _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); assertEq(_countEffect(battleKey, 0, 0, address(singletonAbility)), 1, "should still have exactly 1 effect"); @@ -170,7 +170,7 @@ contract InlineAbilityParityTest is Test, BattleHelper { // Turn 0 _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Execute 2 attack turns — AfterDamage should increment counter each time @@ -205,7 +205,7 @@ contract InlineAbilityParityTest is Test, BattleHelper { // Turn 0 _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // EffectAbility adds dummyEffect — verify it's registered @@ -233,14 +233,14 @@ contract InlineAbilityParityTest is Test, BattleHelper { // Turn 0: switch in mon 0 (inline) _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); assertTrue(_hasEffect(battleKey, 0, 0, address(singletonAbility)), "alice mon 0 should have inline effect"); // Alice switches to mon 1 (external) _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); assertTrue(_hasEffect(battleKey, 0, 1, address(dummyEffect)), "alice mon 1 should have external effect"); } @@ -260,7 +260,7 @@ contract InlineAbilityParityTest is Test, BattleHelper { _commitRevealExecuteForAliceAndBob( engine, commitManager, battleKey, - SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); assertTrue(_hasEffect(battleKey, 0, 0, address(singletonAbility)), "p0 should have inline effect"); diff --git a/test/InlineEngineGasTest.sol b/test/InlineEngineGasTest.sol index 579b6e95..1c447f45 100644 --- a/test/InlineEngineGasTest.sol +++ b/test/InlineEngineGasTest.sol @@ -60,8 +60,14 @@ contract InlineEngineGasTest is Test, BattleHelper { uint256 constant MONS_PER_TEAM = 4; uint256 constant MOVES_PER_MON = 4; - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint240) { - return uint240(playerIndex | (monIndex << 60) | (statIndex << 120) | (uint256(uint32(boostAmount)) << 180)); + // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] + function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { + return uint16( + (playerIndex & 0x1) + | ((monIndex & 0x7) << 1) + | ((statIndex & 0xF) << 4) + | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) + ); } function setUp() public { @@ -162,26 +168,26 @@ contract InlineEngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("FirstBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 1, 0, 0); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(0), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(0), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 3, NO_OP_MOVE_INDEX, 0, 0); vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); uint256 firstBattleGas = vm.stopSnapshotGas("FirstBattle"); @@ -209,27 +215,27 @@ contract InlineEngineGasTest is Test, BattleHelper { console.log("After setup 2 - packedP1EffectsCount:", cfgAfterSetup2.packedP1EffectsCount); vm.startSnapshotGas("SecondBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, 1, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, 2, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, SWITCH_MOVE_INDEX, 2, uint16(1), 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, NO_OP_MOVE_INDEX, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, NO_OP_MOVE_INDEX, 3, 0, _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, NO_OP_MOVE_INDEX, 0, 0, 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 3, 3, _packStatBoost(0, 2, uint256(MonStateIndexName.Attack), int32(90)), _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); vm.startPrank(BOB); - commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey2, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); uint256 secondBattleGas = vm.stopSnapshotGas("SecondBattle"); @@ -252,26 +258,26 @@ contract InlineEngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("ThirdBattle"); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 0, 1, 0, 0); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(0), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(0), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, 3, NO_OP_MOVE_INDEX, 0, 0); vm.startPrank(BOB); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); vm.startPrank(ALICE); - commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint240(3), true); + commitManager.revealMove(battleKey3, SWITCH_MOVE_INDEX, 0, uint16(3), true); engine.resetCallContext(); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); uint256 thirdBattleGas = vm.stopSnapshotGas("ThirdBattle"); @@ -338,7 +344,7 @@ contract InlineEngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Battle1_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey1, 0, 0, 0, 0); uint256 execute1 = vm.stopSnapshotGas("Battle1_Execute"); @@ -350,7 +356,7 @@ contract InlineEngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Battle2_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, 0, 0, 0, 0); uint256 execute2 = vm.stopSnapshotGas("Battle2_Execute"); @@ -412,7 +418,7 @@ contract InlineEngineGasTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("B1_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey1, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); (BattleConfigView memory cfgAfterSwitch,) = inlineEngine.getBattle(battleKey1); console.log("After B1 switch - globalEffectsLength:", cfgAfterSwitch.globalEffectsLength); @@ -445,7 +451,7 @@ contract InlineEngineGasTest is Test, BattleHelper { console.log("After B2 setup - packedP1EffectsCount:", cfg2.packedP1EffectsCount); vm.startSnapshotGas("B2_Execute"); - _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, 0, 0, 0, 0); _commitRevealExecuteForEngine(inlineEngine, inlineCommitManager, battleKey2, 1, 1, 0, 0); uint256 execute2 = vm.stopSnapshotGas("B2_Execute"); @@ -525,10 +531,10 @@ contract InlineEngineGasTest is Test, BattleHelper { bytes32 battleKey, uint8 aliceMoveIndex, uint8 bobMoveIndex, - uint240 aliceExtraData, - uint240 bobExtraData + uint16 aliceExtraData, + uint16 bobExtraData ) internal { - bytes32 salt = ""; + uint104 salt = 0; bytes32 aliceMoveHash = keccak256(abi.encodePacked(aliceMoveIndex, salt, aliceExtraData)); bytes32 bobMoveHash = keccak256(abi.encodePacked(bobMoveIndex, salt, bobExtraData)); uint256 turnId = eng.getTurnIdForBattleState(battleKey); @@ -588,8 +594,14 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { return ("SignedCommitManager", "1"); } - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint240) { - return uint240(playerIndex | (monIndex << 60) | (statIndex << 120) | (uint256(uint32(boostAmount)) << 180)); + // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] + function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { + return uint16( + (playerIndex & 0x1) + | ((monIndex & 0x7) << 1) + | ((statIndex & 0xF) << 4) + | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) + ); } function setUp() public { @@ -651,8 +663,8 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { uint64 turnId, bytes32 committerMoveHash, uint8 revealerMoveIndex, - bytes32 revealerSalt, - uint240 revealerExtraData + uint104 revealerSalt, + uint16 revealerExtraData ) internal view returns (bytes memory) { bytes32 domainSeparator = keccak256( abi.encode( @@ -678,6 +690,28 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { return abi.encodePacked(r, s, v); } + function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) + internal + view + returns (bytes memory) + { + bytes32 domainSeparator = keccak256( + abi.encode( + _DOMAIN_TYPEHASH, + keccak256("SignedCommitManager"), + keccak256("1"), + block.chainid, + address(signedCommitManager) + ) + ); + bytes32 structHash = SignedCommitLib.hashSignedCommit( + SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + /// @dev Executes a two-player turn in 1 TX via executeWithDualSignedMoves. /// p0Move/p1Move semantics match _commitRevealExecuteForAliceAndBob so the /// battle scripts can be transcribed directly from the non-optimized test. @@ -685,17 +719,18 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { bytes32 battleKey, uint8 p0MoveIndex, uint8 p1MoveIndex, - uint240 p0ExtraData, - uint240 p1ExtraData + uint16 p0ExtraData, + uint16 p1ExtraData ) internal { uint64 turnId = uint64(engine.getTurnIdForBattleState(battleKey)); - bytes32 committerSalt = keccak256(abi.encode("committer", battleKey, turnId)); - bytes32 revealerSalt = keccak256(abi.encode("revealer", battleKey, turnId)); + uint104 committerSalt = uint104(uint256(keccak256(abi.encode("committer", battleKey, turnId)))); + uint104 revealerSalt = uint104(uint256(keccak256(abi.encode("revealer", battleKey, turnId)))); uint8 committerMoveIndex; - uint240 committerExtraData; + uint16 committerExtraData; uint8 revealerMoveIndex; - uint240 revealerExtraData; + uint16 revealerExtraData; + uint256 committerPk; uint256 revealerPk; address committer; @@ -704,6 +739,7 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { committerExtraData = p0ExtraData; revealerMoveIndex = p1MoveIndex; revealerExtraData = p1ExtraData; + committerPk = P0_PK; revealerPk = P1_PK; committer = p0; } else { @@ -711,12 +747,14 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { committerExtraData = p1ExtraData; revealerMoveIndex = p0MoveIndex; revealerExtraData = p0ExtraData; + committerPk = P1_PK; revealerPk = P0_PK; committer = p1; } bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); + bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData @@ -727,16 +765,16 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { battleKey, committerMoveIndex, committerSalt, committerExtraData, revealerMoveIndex, revealerSalt, revealerExtraData, - revealerSig + committerSig, revealerSig ); engine.resetCallContext(); } /// @dev Single-player forced switch after a KO. This uses the optimized /// SignedCommitManager path because there is no hidden opponent move to reveal. - function _fastSwitchReveal(bytes32 battleKey, bool isP0, uint240 extraData) internal { + function _fastSwitchReveal(bytes32 battleKey, bool isP0, uint16 extraData) internal { vm.prank(isP0 ? p0 : p1); - signedCommitManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), extraData); + signedCommitManager.executeSinglePlayerMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), extraData); engine.resetCallContext(); } @@ -768,43 +806,43 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { bytes32 oldFlowBattleKey = _startBattleFullyOptimized(ruleset); vm.warp(vm.getBlockTimestamp() + 1); - _fastTurn(oldFlowBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); - _fastTurn(oldFlowBattleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(oldFlowBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); + _fastTurn(oldFlowBattleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0)); assertEq(engine.getPlayerSwitchForTurnFlagForBattleState(oldFlowBattleKey), 1); vm.prank(p1); uint256 gasBefore = gasleft(); - signedCommitManager.revealMove(oldFlowBattleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(1), true); + signedCommitManager.revealMove(oldFlowBattleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), true); uint256 oldFlowGas = gasBefore - gasleft(); engine.resetCallContext(); - _fastTurn(oldFlowBattleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(oldFlowBattleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0)); assertEq(engine.getPlayerSwitchForTurnFlagForBattleState(oldFlowBattleKey), 1); vm.prank(p1); gasBefore = gasleft(); - signedCommitManager.revealMove(oldFlowBattleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(2), true); + signedCommitManager.revealMove(oldFlowBattleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(2), true); uint256 oldFlowSecondGas = gasBefore - gasleft(); engine.resetCallContext(); bytes32 fastPathBattleKey = _startBattleFullyOptimized(ruleset); vm.warp(vm.getBlockTimestamp() + 1); - _fastTurn(fastPathBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); - _fastTurn(fastPathBattleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(fastPathBattleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); + _fastTurn(fastPathBattleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0)); assertEq(engine.getPlayerSwitchForTurnFlagForBattleState(fastPathBattleKey), 1); vm.prank(p1); gasBefore = gasleft(); - signedCommitManager.executeSinglePlayerMove(fastPathBattleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(1)); + signedCommitManager.executeSinglePlayerMove(fastPathBattleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1)); uint256 fastPathGas = gasBefore - gasleft(); engine.resetCallContext(); - _fastTurn(fastPathBattleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(fastPathBattleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0)); assertEq(engine.getPlayerSwitchForTurnFlagForBattleState(fastPathBattleKey), 1); vm.prank(p1); gasBefore = gasleft(); - signedCommitManager.executeSinglePlayerMove(fastPathBattleKey, SWITCH_MOVE_INDEX, bytes32(0), uint240(2)); + signedCommitManager.executeSinglePlayerMove(fastPathBattleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(2)); uint256 fastPathSecondGas = gasBefore - gasleft(); engine.resetCallContext(); @@ -857,19 +895,19 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Fast_Battle1"); - _fastTurn(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _fastTurn(battleKey, 0, 1, 0, 0); - _fastTurn(battleKey, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _fastTurn(battleKey, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); - _fastSwitchReveal(battleKey, true, uint240(0)); + _fastSwitchReveal(battleKey, true, uint16(0)); _fastTurn(battleKey, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _fastTurn(battleKey, 3, NO_OP_MOVE_INDEX, 0, 0); - _fastSwitchReveal(battleKey, false, uint240(1)); + _fastSwitchReveal(battleKey, false, uint16(1)); _fastTurn(battleKey, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); - _fastSwitchReveal(battleKey, true, uint240(2)); + _fastSwitchReveal(battleKey, true, uint16(2)); _fastTurn(battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); - _fastSwitchReveal(battleKey, true, uint240(3)); + _fastSwitchReveal(battleKey, true, uint16(3)); _fastTurn(battleKey, NO_OP_MOVE_INDEX, 3, 0, 0); uint256 firstBattleGas = vm.stopSnapshotGas("Fast_Battle1"); @@ -891,20 +929,20 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Fast_Battle2"); - _fastTurn(battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _fastTurn(battleKey2, 3, 1, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _fastTurn(battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); - _fastSwitchReveal(battleKey2, false, uint240(1)); - _fastTurn(battleKey2, SWITCH_MOVE_INDEX, 2, uint240(1), 0); + _fastSwitchReveal(battleKey2, false, uint16(1)); + _fastTurn(battleKey2, SWITCH_MOVE_INDEX, 2, uint16(1), 0); _fastTurn(battleKey2, 3, NO_OP_MOVE_INDEX, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); _fastTurn(battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); - _fastSwitchReveal(battleKey2, false, uint240(2)); + _fastSwitchReveal(battleKey2, false, uint16(2)); _fastTurn(battleKey2, NO_OP_MOVE_INDEX, 3, 0, _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey2, NO_OP_MOVE_INDEX, 0, 0, 0); - _fastSwitchReveal(battleKey2, true, uint240(2)); + _fastSwitchReveal(battleKey2, true, uint16(2)); _fastTurn(battleKey2, 3, 3, _packStatBoost(0, 2, uint256(MonStateIndexName.Attack), int32(90)), _packStatBoost(1, 2, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); - _fastSwitchReveal(battleKey2, false, uint240(3)); + _fastSwitchReveal(battleKey2, false, uint16(3)); _fastTurn(battleKey2, 0, NO_OP_MOVE_INDEX, 0, 0); uint256 secondBattleGas = vm.stopSnapshotGas("Fast_Battle2"); @@ -926,19 +964,19 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { vm.warp(vm.getBlockTimestamp() + 1); vm.startSnapshotGas("Fast_Battle3"); - _fastTurn(battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(battleKey3, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _fastTurn(battleKey3, 0, 1, 0, 0); - _fastTurn(battleKey3, SWITCH_MOVE_INDEX, 2, uint240(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); + _fastTurn(battleKey3, SWITCH_MOVE_INDEX, 2, uint16(1), _packStatBoost(1, 0, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey3, 2, 3, _packStatBoost(0, 1, uint256(MonStateIndexName.Attack), int32(90)), 0); - _fastSwitchReveal(battleKey3, true, uint240(0)); + _fastSwitchReveal(battleKey3, true, uint16(0)); _fastTurn(battleKey3, 2, NO_OP_MOVE_INDEX, _packStatBoost(0, 0, uint256(MonStateIndexName.Attack), int32(90)), 0); _fastTurn(battleKey3, 3, NO_OP_MOVE_INDEX, 0, 0); - _fastSwitchReveal(battleKey3, false, uint240(1)); + _fastSwitchReveal(battleKey3, false, uint16(1)); _fastTurn(battleKey3, NO_OP_MOVE_INDEX, 2, 0, _packStatBoost(1, 1, uint256(MonStateIndexName.Attack), int32(90))); _fastTurn(battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); - _fastSwitchReveal(battleKey3, true, uint240(2)); + _fastSwitchReveal(battleKey3, true, uint16(2)); _fastTurn(battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); - _fastSwitchReveal(battleKey3, true, uint240(3)); + _fastSwitchReveal(battleKey3, true, uint16(3)); _fastTurn(battleKey3, NO_OP_MOVE_INDEX, 3, 0, 0); uint256 thirdBattleGas = vm.stopSnapshotGas("Fast_Battle3"); diff --git a/test/InlineMoveParityTest.sol b/test/InlineMoveParityTest.sol index 019395f0..7ffaeaf4 100644 --- a/test/InlineMoveParityTest.sol +++ b/test/InlineMoveParityTest.sol @@ -111,9 +111,9 @@ contract InlineMoveParityTest is Test, BattleHelper { } function _doSwitchTurn(bytes32 battleKey) internal { - bytes32 salt = ""; + uint104 salt = 0; uint256 turnId = engine.getTurnIdForBattleState(battleKey); - bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + bytes32 moveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); if (turnId % 2 == 0) { vm.startPrank(ALICE); commitManager.commitMove(battleKey, moveHash); @@ -134,10 +134,10 @@ contract InlineMoveParityTest is Test, BattleHelper { } function _doAttackTurn(bytes32 battleKey, uint8 aliceMove, uint8 bobMove) internal { - bytes32 salt = ""; + uint104 salt = 0; uint256 turnId = engine.getTurnIdForBattleState(battleKey); if (turnId % 2 == 0) { - bytes32 moveHash = keccak256(abi.encodePacked(aliceMove, salt, uint240(0))); + bytes32 moveHash = keccak256(abi.encodePacked(aliceMove, salt, uint16(0))); vm.startPrank(ALICE); commitManager.commitMove(battleKey, moveHash); vm.startPrank(BOB); @@ -145,7 +145,7 @@ contract InlineMoveParityTest is Test, BattleHelper { vm.startPrank(ALICE); commitManager.revealMove(battleKey, aliceMove, salt, 0, true); } else { - bytes32 moveHash = keccak256(abi.encodePacked(bobMove, salt, uint240(0))); + bytes32 moveHash = keccak256(abi.encodePacked(bobMove, salt, uint16(0))); vm.startPrank(BOB); commitManager.commitMove(battleKey, moveHash); vm.startPrank(ALICE); diff --git a/test/InlineValidationTest.sol b/test/InlineValidationTest.sol index 823bd40c..229f9083 100644 --- a/test/InlineValidationTest.sol +++ b/test/InlineValidationTest.sol @@ -125,9 +125,9 @@ contract InlineValidationTest is Test, BattleHelper { bytes32 battleKey = _startBattleWithInlineValidation(); // Both players switch in mon 0 - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); - bytes32 p1MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); + bytes32 p1MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); @@ -150,8 +150,8 @@ contract InlineValidationTest is Test, BattleHelper { bytes32 battleKey = _startBattleWithInlineValidation(); // Both players switch in mon 0 - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); @@ -161,7 +161,7 @@ contract InlineValidationTest is Test, BattleHelper { commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, salt, 0, true); engine.resetCallContext(); // Now use move 0 (attack) - bytes32 p0AttackHash = keccak256(abi.encodePacked(uint8(0), salt, uint240(0))); + bytes32 p0AttackHash = keccak256(abi.encodePacked(uint8(0), salt, uint16(0))); vm.startPrank(p1); commitManager.commitMove(battleKey, p0AttackHash); @@ -180,8 +180,8 @@ contract InlineValidationTest is Test, BattleHelper { bytes32 battleKey = _startBattleWithInlineValidation(); // Both players switch in mon 0 - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); @@ -192,7 +192,7 @@ contract InlineValidationTest is Test, BattleHelper { engine.resetCallContext(); // P1 commits turn 1 - try to switch to mon 0 again (invalid - already active) // The inline validation should treat this as invalid and fall through - bytes32 p1InvalidSwitchHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + bytes32 p1InvalidSwitchHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p1); commitManager.commitMove(battleKey, p1InvalidSwitchHash); @@ -213,10 +213,10 @@ contract InlineValidationTest is Test, BattleHelper { /// @notice Test multiple turns with inline validation function test_inlineValidation_multipleRounds() public { bytes32 battleKey = _startBattleWithInlineValidation(); - bytes32 salt = ""; + uint104 salt = 0; // Turn 0: Both switch in mon 0 - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); vm.startPrank(p1); @@ -233,7 +233,7 @@ contract InlineValidationTest is Test, BattleHelper { if (bd.playerSwitchForTurnFlag != 2) break; uint256 turnId = engine.getTurnIdForBattleState(battleKey); - bytes32 attackHash = keccak256(abi.encodePacked(uint8(0), salt, uint240(0))); + bytes32 attackHash = keccak256(abi.encodePacked(uint8(0), salt, uint16(0))); if (turnId % 2 == 0) { vm.startPrank(p0); @@ -290,8 +290,8 @@ contract InlineValidationTest is Test, BattleHelper { bytes32 battleKey = _startBattleWithInlineValidation(); // Complete turn 0 switches - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); vm.startPrank(p1); @@ -321,8 +321,8 @@ contract InlineValidationTest is Test, BattleHelper { bytes32 battleKey = _startBattleWithInlineValidation(); // Complete turn 0 switches - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); vm.startPrank(p1); @@ -419,8 +419,8 @@ contract InlineValidationTest is Test, BattleHelper { (bytes32 battleKey, DefaultValidator externalValidator) = _startBattleWithExternalValidator(); // Complete turn 0 switches - bytes32 salt = ""; - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint240(0))); + uint104 salt = 0; + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, salt, uint16(0))); vm.startPrank(p0); commitManager.commitMove(battleKey, p0MoveHash); vm.startPrank(p1); @@ -488,7 +488,7 @@ contract InlineValidationTest is Test, BattleHelper { // P0 selects mon 0, CPU will randomly select (mockRNG returns 0, so mon 0) mockRNG.setRNG(0); - cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, "", 0); + cpu.selectMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), 0); // Verify both players switched in assertEq(engine.getActiveMonIndexForBattleState(battleKey)[0], 0, "P0 should have mon 0 active"); @@ -504,7 +504,7 @@ contract InlineValidationTest is Test, BattleHelper { // P0 uses attack, CPU will use attack (mockRNG selects index 1 which is the move) mockRNG.setRNG(1); - cpu.selectMove(battleKey, 0, "", 0); + cpu.selectMove(battleKey, 0, uint104(0), 0); // Battle should have advanced to turn 2 uint256 turnId = engine.getTurnIdForBattleState(battleKey); diff --git a/test/MatchmakerTest.sol b/test/MatchmakerTest.sol index 8678845f..54892be9 100644 --- a/test/MatchmakerTest.sol +++ b/test/MatchmakerTest.sol @@ -393,7 +393,7 @@ contract MatchmakerTest is Test, BattleHelper { assertEq(battleData.p1, BOB); // Check that Alice and Bob can commit/reveal/reveal to switch to mon index 0 - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); } function test_fastBattleSucceedsAndNoSubsequentAccept() public { diff --git a/test/SignedCommitManager.t.sol b/test/SignedCommitManager.t.sol index f9220256..dbdc39a4 100644 --- a/test/SignedCommitManager.t.sol +++ b/test/SignedCommitManager.t.sol @@ -144,8 +144,8 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { uint64 turnId, bytes32 committerMoveHash, uint8 revealerMoveIndex, - bytes32 revealerSalt, - uint240 revealerExtraData + uint104 revealerSalt, + uint16 revealerExtraData ) internal view returns (bytes memory) { bytes32 domainSeparator = keccak256( abi.encode( @@ -210,16 +210,16 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { /// @dev Completes a turn using the normal commit-reveal flow. /// Turn 0 uses SWITCH_MOVE_INDEX; subsequent turns use NO_OP_MOVE_INDEX. function _completeTurnNormal(bytes32 battleKey, uint256 turnId) internal { - bytes32 salt = bytes32(turnId + 1); + uint104 salt = uint104(turnId + 1); uint8 moveIndex = turnId == 0 ? SWITCH_MOVE_INDEX : NO_OP_MOVE_INDEX; - bytes32 moveHash = keccak256(abi.encodePacked(moveIndex, salt, uint240(0))); + bytes32 moveHash = keccak256(abi.encodePacked(moveIndex, salt, uint16(0))); if (turnId % 2 == 0) { // p0 commits vm.startPrank(p0); signedCommitManager.commitMove(battleKey, moveHash); vm.startPrank(p1); - signedCommitManager.revealMove(battleKey, moveIndex, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, moveIndex, uint104(0), 0, false); vm.startPrank(p0); signedCommitManager.revealMove(battleKey, moveIndex, salt, 0, true); } else { @@ -227,7 +227,7 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { vm.startPrank(p1); signedCommitManager.commitMove(battleKey, moveHash); vm.startPrank(p0); - signedCommitManager.revealMove(battleKey, moveIndex, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, moveIndex, uint104(0), 0, false); vm.startPrank(p1); signedCommitManager.revealMove(battleKey, moveIndex, salt, 0, true); } @@ -238,44 +238,31 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { /// @dev Completes a turn using the dual-signed flow (1 TX). /// Turn 0 uses SWITCH_MOVE_INDEX; subsequent turns use NO_OP_MOVE_INDEX. function _completeTurnFast(bytes32 battleKey, uint256 turnId) internal { - bytes32 committerSalt = bytes32(turnId + 1); - bytes32 revealerSalt = bytes32(turnId + 2); + uint104 committerSalt = uint104(turnId + 1); + uint104 revealerSalt = uint104(turnId + 2); uint8 moveIndex = turnId == 0 ? SWITCH_MOVE_INDEX : NO_OP_MOVE_INDEX; - bytes32 committerMoveHash = keccak256(abi.encodePacked(moveIndex, committerSalt, uint240(0))); + bytes32 committerMoveHash = keccak256(abi.encodePacked(moveIndex, committerSalt, uint16(0))); - if (turnId % 2 == 0) { - // p0 is committer, p1 is revealer - bytes memory revealerSignature = _signDualReveal( - P1_PK, battleKey, uint64(turnId), committerMoveHash, moveIndex, revealerSalt, 0 - ); - vm.startPrank(p0); - signedCommitManager.executeWithDualSignedMoves( - battleKey, - moveIndex, - committerSalt, - 0, - moveIndex, - revealerSalt, - 0, - revealerSignature - ); - } else { - // p1 is committer, p0 is revealer - bytes memory revealerSignature = _signDualReveal( - P0_PK, battleKey, uint64(turnId), committerMoveHash, moveIndex, revealerSalt, 0 - ); - vm.startPrank(p1); - signedCommitManager.executeWithDualSignedMoves( - battleKey, - moveIndex, - committerSalt, - 0, - moveIndex, - revealerSalt, - 0, - revealerSignature - ); - } + (uint256 committerPk, uint256 revealerPk) = turnId % 2 == 0 ? (P0_PK, P1_PK) : (P1_PK, P0_PK); + bytes memory committerSignature = + _signCommit(committerPk, committerMoveHash, battleKey, uint64(turnId)); + bytes memory revealerSignature = _signDualReveal( + revealerPk, battleKey, uint64(turnId), committerMoveHash, moveIndex, revealerSalt, 0 + ); + + // Caller can be anyone; pick committer for parity with old test setup. + vm.startPrank(turnId % 2 == 0 ? p0 : p1); + signedCommitManager.executeWithDualSignedMoves( + battleKey, + moveIndex, + committerSalt, + 0, + moveIndex, + revealerSalt, + 0, + committerSignature, + revealerSignature + ); vm.stopPrank(); engine.resetCallContext(); } @@ -294,11 +281,12 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint64 turnId = 0; // p0 creates commitment hash off-chain - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - // p1 signs their move + p0's hash - bytes32 p1Salt = bytes32(uint256(2)); + // p0 signs their commitment, p1 signs their move + p0's hash + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, turnId); + uint104 p1Salt = uint104(2); bytes memory p1Signature = _signDualReveal( P1_PK, battleKey, turnId, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -313,6 +301,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { SWITCH_MOVE_INDEX, p1Salt, 0, + p0CommitSig, p1Signature ); @@ -333,10 +322,11 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { // Turn 1: p1 is committer, p0 is revealer uint64 turnId = 1; - bytes32 p1Salt = bytes32(uint256(2)); - bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint240(0))); + uint104 p1Salt = uint104(2); + bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); + bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, turnId); - bytes32 p0Salt = bytes32(uint256(3)); + uint104 p0Salt = uint104(3); bytes memory p0Signature = _signDualReveal( P0_PK, battleKey, turnId, p1MoveHash, NO_OP_MOVE_INDEX, p0Salt, 0 ); @@ -350,6 +340,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { NO_OP_MOVE_INDEX, p0Salt, 0, + p1CommitSig, p0Signature ); @@ -418,7 +409,11 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_invalidSignature() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + + // Valid committer sig, but garbage revealer sig. + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); bytes memory invalidSignature = abi.encodePacked(bytes32(uint256(1)), bytes32(uint256(2)), uint8(27)); vm.startPrank(p0); @@ -429,8 +424,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, invalidSignature ); } @@ -438,12 +434,13 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_wrongSigner() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - // p0 signs instead of p1 (wrong signer - should be revealer p1) + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + // p0 signs the revealer slot instead of p1 (wrong signer - should be revealer p1) bytes memory wrongSignature = _signDualReveal( - P0_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P0_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); vm.startPrank(p0); @@ -454,8 +451,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, wrongSignature ); } @@ -467,13 +465,14 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { _completeTurnNormal(battleKey, 0); _completeTurnNormal(battleKey, 1); - // On turn 2, p0 is committer again. Try to replay a turn 0 signature. - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint240(0))); + // On turn 2, p0 is committer again. Try to replay turn-0 signatures. + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); - // Create signature for turn 0 (not current turn 2) + // Both signatures bound to turnId=0, replayed at turnId=2 + bytes memory turn0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); bytes memory turn0Signature = _signDualReveal( - P1_PK, battleKey, 0, p0MoveHash, NO_OP_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey, 0, p0MoveHash, NO_OP_MOVE_INDEX, uint104(0), 0 ); vm.startPrank(p0); @@ -484,8 +483,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, NO_OP_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + turn0CommitSig, turn0Signature ); } @@ -493,15 +493,16 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_replayAttack_differentBattle() public { bytes32 battleKey1 = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - // Create signature for battle 1 + // Both signatures bound to battle 1 + bytes memory battle1CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey1, 0); bytes memory battle1Signature = _signDualReveal( - P1_PK, battleKey1, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey1, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); - // Start second battle and try to use battle 1's signature + // Start second battle and try to use battle 1's signatures bytes32 battleKey2 = _startBattleWith(address(signedCommitManager)); vm.startPrank(p0); @@ -512,33 +513,146 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + battle1CommitSig, battle1Signature ); } - function test_revert_callerNotCommitter() public { + /// @notice Regression: a revealer alone (without an explicit committer signature) cannot + /// inject a self-chosen committer preimage `P*`. Previously this was blocked only by the + /// `msg.sender == committer` check; now both signatures are mandatory and bind each + /// player independently, so the check holds even under a relayer model. + function test_revert_executeWithDualSigned_unilateralRevealerAttack() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + // Attacker (p1, the revealer for turn 0) picks a preimage P* of their choosing for p0 + uint104 attackerCommitterSalt = uint104(0xdead); + uint16 attackerCommitterExtraData = 0; + uint8 attackerCommitterMoveIndex = SWITCH_MOVE_INDEX; + bytes32 chosenCommitterMoveHash = keccak256( + abi.encodePacked(attackerCommitterMoveIndex, attackerCommitterSalt, attackerCommitterExtraData) + ); + // p1 signs the DualSignedReveal binding themselves to a chosen committer preimage bytes memory p1Signature = _signDualReveal( - P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey, 0, chosenCommitterMoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); - // p1 (revealer) tries to call executeWithDualSignedMoves - should fail - vm.startPrank(p1); - vm.expectRevert(SignedCommitManager.CallerNotCommitter.selector); + // Attacker forges a "committer signature" (signed by themselves, P1, over the same hash). + bytes memory forgedCommitterSig = _signCommit(P1_PK, chosenCommitterMoveHash, battleKey, 0); + + // _startBattleWith leaves an active prank on p0; clear it. + vm.stopPrank(); + + // Submit (from any sender) — committer sig recover will return p1, not p0 → revert. + vm.expectRevert(SignedCommitManager.InvalidSignature.selector); + signedCommitManager.executeWithDualSignedMoves( + battleKey, + attackerCommitterMoveIndex, + attackerCommitterSalt, + attackerCommitterExtraData, + SWITCH_MOVE_INDEX, + uint104(0), + 0, + forgedCommitterSig, + p1Signature + ); + } + + /// @notice Drops the old `msg.sender == committer` check: anyone can submit when both + /// EIP-712 signatures are present and valid (relayer-friendly). + function test_executeWithDualSigned_thirdPartyRelay_succeeds() public { + bytes32 battleKey = _startBattleWith(address(signedCommitManager)); + + uint104 p0Salt = uint104(1); + uint104 p1Salt = uint104(2); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal( + P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 + ); + + // _startBattleWith leaves an active prank on p0; clear it before pranking the relayer. + vm.stopPrank(); + + // A random third party (neither p0 nor p1) can submit the bundle. + address relayer = address(0xCAFE); + vm.prank(relayer); + signedCommitManager.executeWithDualSignedMoves( + battleKey, + SWITCH_MOVE_INDEX, + p0Salt, + 0, + SWITCH_MOVE_INDEX, + p1Salt, + 0, + p0CommitSig, + p1Signature + ); + + assertEq(engine.getTurnIdForBattleState(battleKey), 1, "Turn should advance via relayer"); + } + + /// @notice Wrong committer signer (sig recovers to revealer's address, not committer's) reverts. + function test_revert_executeWithDualSigned_wrongCommitterSigner() public { + bytes32 battleKey = _startBattleWith(address(signedCommitManager)); + + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + + // p1 signs the SignedCommit instead of p0 → recovers to p1, not the committer p0. + bytes memory wrongCommitSig = _signCommit(P1_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal( + P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 + ); + + vm.startPrank(p0); + vm.expectRevert(SignedCommitManager.InvalidSignature.selector); signedCommitManager.executeWithDualSignedMoves( battleKey, SWITCH_MOVE_INDEX, p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + wrongCommitSig, + p1Signature + ); + } + + /// @notice Committer signature over a different `moveHash` than the submitted preimage + /// reverts with InvalidSignature (the recovered hash differs from what the engine computes). + function test_revert_executeWithDualSigned_committerSigForWrongHash() public { + bytes32 battleKey = _startBattleWith(address(signedCommitManager)); + + uint104 p0Salt = uint104(1); + bytes32 p0DifferentMoveHash = + keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); // committer signs over a different move + + bytes memory mismatchedCommitSig = _signCommit(P0_PK, p0DifferentMoveHash, battleKey, 0); + // Revealer signs the same different hash so the revealer side would have validated + bytes memory p1Signature = _signDualReveal( + P1_PK, battleKey, 0, p0DifferentMoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 + ); + + // p0 submits with their REAL move data (SWITCH_MOVE_INDEX, p0Salt, 0). Engine recomputes + // committerMoveHash from those fields → does not equal `p0DifferentMoveHash`. Committer sig + // recovery against the recomputed hash returns a non-p0 address → InvalidSignature. + vm.startPrank(p0); + vm.expectRevert(SignedCommitManager.InvalidSignature.selector); + signedCommitManager.executeWithDualSignedMoves( + battleKey, + SWITCH_MOVE_INDEX, + p0Salt, + 0, + SWITCH_MOVE_INDEX, + uint104(0), + 0, + mismatchedCommitSig, p1Signature ); } @@ -550,11 +664,12 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_battleNotStarted() public { bytes32 fakeBattleKey = bytes32(uint256(123)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, fakeBattleKey, 0); bytes memory p1Signature = _signDualReveal( - P1_PK, fakeBattleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, fakeBattleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); vm.startPrank(p0); @@ -565,8 +680,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, p1Signature ); } @@ -577,14 +693,16 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { // Execute turn 0 with dual-signed flow _completeTurnFast(battleKey, 0); - // After turn 0, we're now on turn 1 where p1 is committer - // Try to replay with a turn 0 signature - fails because: - // 1. Turn has advanced, so signature turnId (0) doesn't match current turnId (1) - bytes32 p1Salt = bytes32(uint256(99)); - bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint240(0))); + // After turn 0, we're now on turn 1 where p1 is committer. + // Try to replay with turn-0 signatures - fails because turnId in sigs (0) doesn't + // match current turnId (1). + uint104 p1Salt = uint104(99); + bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); + // Both signatures are bound to turnId=0 (replay attempt) + bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, 0); bytes memory p0Signature = _signDualReveal( - P0_PK, battleKey, 0, p1MoveHash, NO_OP_MOVE_INDEX, bytes32(0), 0 + P0_PK, battleKey, 0, p1MoveHash, NO_OP_MOVE_INDEX, uint104(0), 0 ); vm.startPrank(p1); @@ -595,8 +713,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p1Salt, 0, NO_OP_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p1CommitSig, p0Signature ); } @@ -604,11 +723,12 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_replayPrevented_sameBlockAttempt() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); bytes memory p1Signature = _signDualReveal( - P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); vm.startPrank(p0); @@ -618,22 +738,24 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, p1Signature ); - // After execution, turn advances to 1. On turn 1, p1 is committer, not p0. - // So if p0 tries to call again, it fails with CallerNotCommitter (turn parity changed) - vm.expectRevert(SignedCommitManager.CallerNotCommitter.selector); + // After execution, turn advances to 1. Replaying the same signatures (turnId=0) at + // turnId=1 fails on the committer signature recovery — sig was bound to turn 0. + vm.expectRevert(SignedCommitManager.InvalidSignature.selector); signedCommitManager.executeWithDualSignedMoves( battleKey, SWITCH_MOVE_INDEX, p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, p1Signature ); } @@ -642,16 +764,20 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); // p0's actual move data - bytes32 p0Salt = bytes32(uint256(1)); + uint104 p0Salt = uint104(1); + bytes32 p0RealMoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - // p1 signs over a DIFFERENT hash than what p0 will submit - bytes32 fakeP0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(999)), uint240(0))); + // p0 signs the commitment for the REAL move hash (matches what they'll submit) + bytes memory p0CommitSig = _signCommit(P0_PK, p0RealMoveHash, battleKey, 0); + // p1 signs over a DIFFERENT hash than what p0 will submit + bytes32 fakeP0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(999), uint16(0))); bytes memory p1Signature = _signDualReveal( - P1_PK, battleKey, 0, fakeP0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey, 0, fakeP0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); - // p0 tries to submit with their real move data, but the hash won't match what p1 signed + // p0 tries to submit with their real move data: committer sig validates (matches + // p0RealMoveHash), but revealer sig was over fakeP0MoveHash → revealer recovery fails. vm.startPrank(p0); vm.expectRevert(SignedCommitManager.InvalidSignature.selector); signedCommitManager.executeWithDualSignedMoves( @@ -660,8 +786,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, SWITCH_MOVE_INDEX, - bytes32(0), + uint104(0), 0, + p0CommitSig, p1Signature ); } @@ -669,12 +796,13 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_revert_revealerMoveMismatch() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); // p1 signs with SWITCH_MOVE_INDEX bytes memory p1Signature = _signDualReveal( - P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, bytes32(0), 0 + P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); // p0 tries to submit with different move for p1 (NO_OP instead of SWITCH) @@ -686,8 +814,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { p0Salt, 0, NO_OP_MOVE_INDEX, // Different from what p1 signed! - bytes32(0), + uint104(0), 0, + p0CommitSig, p1Signature ); } @@ -700,8 +829,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); // Turn 0: p0 is committer, p1 is revealer - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // p0 signs their commitment bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); @@ -716,7 +845,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { assertEq(storedTurnId, 0, "Turn ID not stored correctly"); // Now p1 can reveal normally - signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), 0, false); // p0 reveals to complete the turn vm.startPrank(p0); @@ -733,8 +862,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { _completeTurnNormal(battleKey, 0); // Turn 1: p1 is committer, p0 is revealer - bytes32 p1Salt = bytes32(uint256(2)); - bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint240(0))); + uint104 p1Salt = uint104(2); + bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); // p1 signs their commitment bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, 1); @@ -749,7 +878,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { assertEq(storedTurnId, 1, "Turn ID not stored correctly"); // Now p0 can reveal - signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0, false); // p1 reveals to complete the turn vm.startPrank(p1); @@ -761,8 +890,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_anyoneCanSubmit() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); // Even p0 themselves can submit their own signed commitment @@ -778,7 +907,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_revert_wrongSigner() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p1 signs instead of p0 (wrong signer) bytes memory wrongSig = _signCommit(P1_PK, p0MoveHash, battleKey, 0); @@ -791,7 +920,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_revert_wrongTurn() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p0 signs for turn 1 instead of turn 0 bytes memory wrongTurnSig = _signCommit(P0_PK, p0MoveHash, battleKey, 1); @@ -805,7 +934,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 battleKey1 = _startBattleWith(address(signedCommitManager)); bytes32 battleKey2 = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p0 signs for battle 1 bytes memory battle1Sig = _signCommit(P0_PK, p0MoveHash, battleKey1, 0); @@ -819,7 +948,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_revert_alreadyCommitted() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); // First commit succeeds @@ -833,7 +962,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_revert_battleNotStarted() public { bytes32 fakeBattleKey = bytes32(uint256(123)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, fakeBattleKey, 0); vm.startPrank(p1); @@ -844,7 +973,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_afterNormalCommit_reverts() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p0 commits normally vm.startPrank(p0); @@ -873,18 +1002,20 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { bytes32 battleKey, uint64 turnId, uint8 committerMoveIndex, - uint240 committerExtraData, + uint16 committerExtraData, uint8 revealerMoveIndex, - uint240 revealerExtraData + uint16 revealerExtraData ) internal { - bytes32 committerSalt = bytes32(uint256(turnId + 1)); - bytes32 revealerSalt = bytes32(uint256(turnId + 2)); + uint104 committerSalt = uint104(turnId + 1); + uint104 revealerSalt = uint104(turnId + 2); bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); // Committer is p0 on even turns, p1 on odd turns. - (uint256 revealerPk, address committerAddr) = turnId % 2 == 0 ? (P1_PK, p0) : (P0_PK, p1); + (uint256 committerPk, uint256 revealerPk, address committerAddr) = + turnId % 2 == 0 ? (P0_PK, P1_PK, p0) : (P1_PK, P0_PK, p1); + bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); @@ -898,6 +1029,7 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { revealerMoveIndex, revealerSalt, revealerExtraData, + committerSig, revealerSig ); vm.stopPrank(); @@ -911,7 +1043,7 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { // p0 sends a valid switch to mon 1, p1 sends NO_OP (would have been rejected at reveal in // the old flow). Engine must force p1 to switch-to-mon-0. - _executeDualSigned(battleKey, 0, SWITCH_MOVE_INDEX, uint240(1), NO_OP_MOVE_INDEX, 0); + _executeDualSigned(battleKey, 0, SWITCH_MOVE_INDEX, uint16(1), NO_OP_MOVE_INDEX, 0); assertEq(engine.getTurnIdForBattleState(battleKey), 1, "turn should advance"); uint256[] memory active = engine.getActiveMonIndexForBattleState(battleKey); @@ -971,7 +1103,7 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { // Turn 1: p1 commits SWITCH with an out-of-bounds target (team size is 2, submit 99). // p0 NO_OPs. p1's active mon should stay at 0 (switch is a no-op), not wrap to 99 & 0xFF. - _executeDualSigned(battleKey, 1, SWITCH_MOVE_INDEX, uint240(99), NO_OP_MOVE_INDEX, 0); + _executeDualSigned(battleKey, 1, SWITCH_MOVE_INDEX, uint16(99), NO_OP_MOVE_INDEX, 0); assertEq(engine.getTurnIdForBattleState(battleKey), 2, "turn should advance"); uint256[] memory active = engine.getActiveMonIndexForBattleState(battleKey); @@ -983,7 +1115,7 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); // Turn 0: p0 switches to mon 1, p1 to mon 0. - _executeDualSigned(battleKey, 0, SWITCH_MOVE_INDEX, uint240(1), SWITCH_MOVE_INDEX, 0); + _executeDualSigned(battleKey, 0, SWITCH_MOVE_INDEX, uint16(1), SWITCH_MOVE_INDEX, 0); assertEq(engine.getActiveMonIndexForBattleState(battleKey)[0], 1); // Turn 1: p1 is committer. p1 tries to switch to their own active mon (0). p0 NO_OP. diff --git a/test/SignedCommitManagerGasBenchmark.t.sol b/test/SignedCommitManagerGasBenchmark.t.sol index 811bbc60..25cc12ac 100644 --- a/test/SignedCommitManagerGasBenchmark.t.sol +++ b/test/SignedCommitManagerGasBenchmark.t.sol @@ -26,7 +26,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { function test_gasBenchmark_normalFlow_cold() public { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); vm.startPrank(p0); uint256 gasBefore = gasleft(); @@ -35,12 +35,12 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { vm.startPrank(p1); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), 0, false); gasUsed_normalFlow_cold_reveal1 = gasBefore - gasleft(); vm.startPrank(p0); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, bytes32(uint256(1)), 0, true); + signedCommitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(1), 0, true); gasUsed_normalFlow_cold_reveal2 = gasBefore - gasleft(); emit log_named_uint("Normal Flow (Cold) - Commit (Alice)", gasUsed_normalFlow_cold_commit); @@ -55,11 +55,12 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); // Prepare move data - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p1Salt = bytes32(uint256(2)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + uint104 p1Salt = uint104(2); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - // p1 (revealer) signs their move + p0's hash + // Both players sign off-chain + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); bytes memory p1Signature = _signDualReveal( P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -75,6 +76,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { SWITCH_MOVE_INDEX, p1Salt, 0, + p0CommitSig, p1Signature ); gasUsed_dualSignedFlow_cold = gasBefore - gasleft(); @@ -90,7 +92,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { _completeTurnNormal(battleKey, 1); // Turn 2 (warm storage - p0 commits again) - bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, bytes32(uint256(100)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, uint104(100), uint16(0))); vm.startPrank(p0); uint256 gasBefore = gasleft(); @@ -99,12 +101,12 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { vm.startPrank(p1); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, uint104(0), 0, false); gasUsed_normalFlow_warm_reveal1 = gasBefore - gasleft(); vm.startPrank(p0); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, bytes32(uint256(100)), 0, true); + signedCommitManager.revealMove(battleKey, NO_OP_MOVE_INDEX, uint104(100), 0, true); gasUsed_normalFlow_warm_reveal2 = gasBefore - gasleft(); emit log_named_uint("Normal Flow (Warm) - Commit (Alice)", gasUsed_normalFlow_warm_commit); @@ -122,10 +124,11 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { _completeTurnNormal(battleKey, 1); // Turn 2 with dual-signed flow (warm storage) - bytes32 p0Salt = bytes32(uint256(100)); - bytes32 p1Salt = bytes32(uint256(101)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(100); + uint104 p1Salt = uint104(101); + bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 2); bytes memory p1Signature = _signDualReveal( P1_PK, battleKey, 2, p0MoveHash, NO_OP_MOVE_INDEX, p1Salt, 0 ); @@ -140,6 +143,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { NO_OP_MOVE_INDEX, p1Salt, 0, + p0CommitSig, p1Signature ); gasUsed_dualSignedFlow_warm = gasBefore - gasleft(); @@ -156,7 +160,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { // Normal flow cold (3 TXs) { - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, bytes32(uint256(1)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); vm.startPrank(p0); uint256 gasBefore = gasleft(); @@ -165,12 +169,12 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { vm.startPrank(p1); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey1, SWITCH_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey1, SWITCH_MOVE_INDEX, uint104(0), 0, false); gasUsed_normalFlow_cold_reveal1 = gasBefore - gasleft(); vm.startPrank(p0); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey1, SWITCH_MOVE_INDEX, bytes32(uint256(1)), 0, true); + signedCommitManager.revealMove(battleKey1, SWITCH_MOVE_INDEX, uint104(1), 0, true); gasUsed_normalFlow_cold_reveal2 = gasBefore - gasleft(); } @@ -178,10 +182,11 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { // Reset transient first so a stale execute from battleKey1 above doesn't pollute battleKey2's measurement. engine.resetCallContext(); { - bytes32 p0Salt = bytes32(uint256(1)); - bytes32 p1Salt = bytes32(uint256(2)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(1); + uint104 p1Salt = uint104(2); + bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey2, 0); bytes memory p1Signature = _signDualReveal( P1_PK, battleKey2, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -196,6 +201,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { SWITCH_MOVE_INDEX, p1Salt, 0, + p0CommitSig, p1Signature ); gasUsed_dualSignedFlow_cold = gasBefore - gasleft(); @@ -210,7 +216,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { // Normal flow warm (turn 2) { - bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, bytes32(uint256(100)), uint240(0))); + bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, uint104(100), uint16(0))); vm.startPrank(p0); uint256 gasBefore = gasleft(); @@ -219,21 +225,22 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { vm.startPrank(p1); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey1, NO_OP_MOVE_INDEX, bytes32(0), 0, false); + signedCommitManager.revealMove(battleKey1, NO_OP_MOVE_INDEX, uint104(0), 0, false); gasUsed_normalFlow_warm_reveal1 = gasBefore - gasleft(); vm.startPrank(p0); gasBefore = gasleft(); - signedCommitManager.revealMove(battleKey1, NO_OP_MOVE_INDEX, bytes32(uint256(100)), 0, true); + signedCommitManager.revealMove(battleKey1, NO_OP_MOVE_INDEX, uint104(100), 0, true); gasUsed_normalFlow_warm_reveal2 = gasBefore - gasleft(); } // Dual-signed flow warm (turn 2) { - bytes32 p0Salt = bytes32(uint256(100)); - bytes32 p1Salt = bytes32(uint256(101)); - bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint240(0))); + uint104 p0Salt = uint104(100); + uint104 p1Salt = uint104(101); + bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); + bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey2, 2); bytes memory p1Signature = _signDualReveal( P1_PK, battleKey2, 2, p0MoveHash, NO_OP_MOVE_INDEX, p1Salt, 0 ); @@ -248,6 +255,7 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { NO_OP_MOVE_INDEX, p1Salt, 0, + p0CommitSig, p1Signature ); gasUsed_dualSignedFlow_warm = gasBefore - gasleft(); diff --git a/test/StandardAttackPvPGasTest.sol b/test/StandardAttackPvPGasTest.sol index 27fda2a1..9f981f30 100644 --- a/test/StandardAttackPvPGasTest.sol +++ b/test/StandardAttackPvPGasTest.sol @@ -122,8 +122,8 @@ contract StandardAttackPvPGasTest is Test, EIP712 { uint64 turnId, bytes32 committerMoveHash, uint8 revealerMoveIndex, - bytes32 revealerSalt, - uint240 revealerExtraData + uint104 revealerSalt, + uint16 revealerExtraData ) internal view returns (bytes memory) { bytes32 domainSeparator = keccak256( abi.encode( @@ -149,21 +149,44 @@ contract StandardAttackPvPGasTest is Test, EIP712 { return abi.encodePacked(r, s, v); } + function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) + internal + view + returns (bytes memory) + { + bytes32 domainSeparator = keccak256( + abi.encode( + _DOMAIN_TYPEHASH, + keccak256("SignedCommitManager"), + keccak256("1"), + block.chainid, + address(signedCommitManager) + ) + ); + bytes32 structHash = SignedCommitLib.hashSignedCommit( + SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + function _fastTurn( bytes32 battleKey, uint8 p0MoveIndex, uint8 p1MoveIndex, - uint240 p0ExtraData, - uint240 p1ExtraData + uint16 p0ExtraData, + uint16 p1ExtraData ) internal { uint64 turnId = uint64(engine.getTurnIdForBattleState(battleKey)); - bytes32 committerSalt = keccak256(abi.encode("committer", battleKey, turnId)); - bytes32 revealerSalt = keccak256(abi.encode("revealer", battleKey, turnId)); + uint104 committerSalt = uint104(uint256(keccak256(abi.encode("committer", battleKey, turnId)))); + uint104 revealerSalt = uint104(uint256(keccak256(abi.encode("revealer", battleKey, turnId)))); uint8 committerMoveIndex; - uint240 committerExtraData; + uint16 committerExtraData; uint8 revealerMoveIndex; - uint240 revealerExtraData; + uint16 revealerExtraData; + uint256 committerPk; uint256 revealerPk; address committer; @@ -172,6 +195,7 @@ contract StandardAttackPvPGasTest is Test, EIP712 { committerExtraData = p0ExtraData; revealerMoveIndex = p1MoveIndex; revealerExtraData = p1ExtraData; + committerPk = P0_PK; revealerPk = P1_PK; committer = p0; } else { @@ -179,12 +203,14 @@ contract StandardAttackPvPGasTest is Test, EIP712 { committerExtraData = p1ExtraData; revealerMoveIndex = p0MoveIndex; revealerExtraData = p0ExtraData; + committerPk = P1_PK; revealerPk = P0_PK; committer = p1; } bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); + bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); @@ -198,6 +224,7 @@ contract StandardAttackPvPGasTest is Test, EIP712 { revealerMoveIndex, revealerSalt, revealerExtraData, + committerSig, revealerSig ); engine.resetCallContext(); @@ -280,7 +307,7 @@ contract StandardAttackPvPGasTest is Test, EIP712 { // Turn 0: lead-in switch. vm.startSnapshotGas("Turn0_Lead"); - _fastTurn(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _fastTurn(battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); uint256 turn0 = vm.stopSnapshotGas("Turn0_Lead"); // Turns 1-4: pure damage trades. Both players use move 0 / move 1 alternately. diff --git a/test/abstract/BattleHelper.sol b/test/abstract/BattleHelper.sol index 45caac16..bca3115f 100644 --- a/test/abstract/BattleHelper.sol +++ b/test/abstract/BattleHelper.sol @@ -25,10 +25,10 @@ abstract contract BattleHelper is Test { bytes32 battleKey, uint8 aliceMoveIndex, uint8 bobMoveIndex, - uint240 aliceExtraData, - uint240 bobExtraData + uint16 aliceExtraData, + uint16 bobExtraData ) internal { - bytes32 salt = ""; + uint104 salt = 0; bytes32 aliceMoveHash = keccak256(abi.encodePacked(aliceMoveIndex, salt, aliceExtraData)); bytes32 bobMoveHash = keccak256(abi.encodePacked(bobMoveIndex, salt, bobExtraData)); // Decide which player commits @@ -64,8 +64,8 @@ abstract contract BattleHelper is Test { DefaultCommitManager commitManager, bytes32 battleKey, uint8 moveIndex, - bytes32 salt, - uint240 extraData + uint104 salt, + uint16 extraData ) internal { commitManager.revealMove(battleKey, moveIndex, salt, extraData, true); vm.stopPrank(); diff --git a/test/effects/EffectTest.sol b/test/effects/EffectTest.sol index 95349ff6..86b657b5 100644 --- a/test/effects/EffectTest.sol +++ b/test/effects/EffectTest.sol @@ -146,7 +146,7 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks, both of them are move index 0 (do frostbite damage) @@ -237,11 +237,11 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice switches to mon index 1, Bob induces frostbite - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), 0); // Check that Alice's new mon at index 0 has taken damage assertEq(engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.Hp), -1); @@ -301,7 +301,7 @@ contract EffectTest is Test, BattleHelper { */ bytes32 battleKey = _startBattle(validatorToUse, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0)); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 0, 0, 0); assertEq(engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Stamina), -1); assertEq(engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina), 0); @@ -316,7 +316,7 @@ contract EffectTest is Test, BattleHelper { assertEq(engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Stamina), -1); // Alice is asleep, Bob does nothing, Alice switches to mon index 1, should be successful mockOracle.setRNG(1); - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0); assertEq(engine.getActiveMonIndexForBattleState(battleKey)[0], 1); } @@ -392,7 +392,7 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks, both of them are move index 0 (inflict panic) @@ -477,7 +477,7 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks, both of them are move index 0 (apply burn status) @@ -628,7 +628,7 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice and Bob both select attacks, both of them are move index 0 (apply zap status) @@ -640,14 +640,14 @@ contract EffectTest is Test, BattleHelper { assertEq(engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.Stamina), -1); // Alice uses Zap, Bob switches to mon index 1 - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, 0, uint240(1)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, 0, uint16(1)); // The move should outspeed the swap, so the swap doesn't happen // So Bob's active mon index should still be 0 assertEq(engine.getActiveMonIndexForBattleState(battleKey)[1], 0); // Alice uses slower Zap, Bob switches to mon index 1 - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, SWITCH_MOVE_INDEX, 0, uint240(1)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, SWITCH_MOVE_INDEX, 0, uint16(1)); // Bob's active mon index should be 1 (swap goes before getting Zapped) assertEq(engine.getActiveMonIndexForBattleState(battleKey)[1], 1); @@ -657,7 +657,7 @@ contract EffectTest is Test, BattleHelper { assertEq(zapEffects.length, 1); // Alice does nothing, Bob attempts to switch to mon index 1, which should succeed because Zap allows switches - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(0)); // Check that Bob's active mon index is now 0, and the effect is still there assertEq(engine.getActiveMonIndexForBattleState(battleKey)[1], 0); @@ -665,7 +665,7 @@ contract EffectTest is Test, BattleHelper { assertEq(zapEffectsAfter.length, 1); // Bob switches back to mon index 1, Alice does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(1)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(1)); // Bob tries to make a move, Alice does nothing, Zap should skip his turn _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 0, 0, 0); @@ -728,7 +728,7 @@ contract EffectTest is Test, BattleHelper { // First move of the game has to be selecting their mons (both index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses NoDamage, Bob does as well @@ -803,7 +803,7 @@ contract EffectTest is Test, BattleHelper { // First move: both switch in their mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify Bob's mon has the heal effect applied (from ability on switch in) diff --git a/test/effects/StatBoosts.t.sol b/test/effects/StatBoosts.t.sol index cb276a67..8ddd614e 100644 --- a/test/effects/StatBoosts.t.sol +++ b/test/effects/StatBoosts.t.sol @@ -38,9 +38,14 @@ contract StatBoostsTest is Test, BattleHelper { StatBoostsMove statBoostMove; DefaultMatchmaker matchmaker; - // Helper to pack StatBoostsMove extraData: lower 60 bits = playerIndex, next 60 bits = monIndex, next 60 bits = statIndex, upper 60 bits = boostAmount - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint240) { - return uint240(playerIndex | (monIndex << 60) | (statIndex << 120) | (uint256(uint32(boostAmount)) << 180)); + // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] + function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { + return uint16( + (playerIndex & 0x1) + | ((monIndex & 0x7) << 1) + | ((statIndex & 0xF) << 4) + | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) + ); } function setUp() public { @@ -112,7 +117,7 @@ contract StatBoostsTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // We'll test Attack stat in detail @@ -193,7 +198,7 @@ contract StatBoostsTest is Test, BattleHelper { battleKey, SWITCH_MOVE_INDEX, // Alice switches NO_OP_MOVE_INDEX, // Bob does nothing - uint240(1), // Alice switches to mon 1 + uint16(1), // Alice switches to mon 1 0 // Bob does nothing ); @@ -215,7 +220,7 @@ contract StatBoostsTest is Test, BattleHelper { battleKey, SWITCH_MOVE_INDEX, // Alice switches NO_OP_MOVE_INDEX, // Bob does nothing - uint240(0), // Alice switches back to mon 0 + uint16(0), // Alice switches back to mon 0 0 // Bob does nothing ); @@ -286,7 +291,7 @@ contract StatBoostsTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Test all stats @@ -335,7 +340,7 @@ contract StatBoostsTest is Test, BattleHelper { battleKey, SWITCH_MOVE_INDEX, // Alice switches NO_OP_MOVE_INDEX, // Bob does nothing - uint240(1), // Alice switches to mon 1 + uint16(1), // Alice switches to mon 1 0 // Bob does nothing ); @@ -389,7 +394,7 @@ contract StatBoostsTest is Test, BattleHelper { // Both players select their first mon (index 0) bytes32 battleKey = _startBattle(validatorToUse, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses stat boost move to boost her mon's special atk 50%, Bob does nothing @@ -429,7 +434,7 @@ contract StatBoostsTest is Test, BattleHelper { battleKey, SWITCH_MOVE_INDEX, // Alice switches NO_OP_MOVE_INDEX, // Bob does nothing - uint240(1), // Alice switches to mon 1 + uint16(1), // Alice switches to mon 1 0 // Bob does nothing ); diff --git a/test/mocks/CustomAttack.sol b/test/mocks/CustomAttack.sol index 0f5913a5..9c4a12c0 100644 --- a/test/mocks/CustomAttack.sol +++ b/test/mocks/CustomAttack.sol @@ -55,7 +55,7 @@ contract CustomAttack is IMoveSet { uint256 attackerPlayerIndex, uint256 attackerMonIndex, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 rng ) external { _standardAttack.move(engine, battleKey, attackerPlayerIndex, attackerMonIndex, defenderMonIndex, extraData, rng); @@ -73,7 +73,7 @@ contract CustomAttack is IMoveSet { return _standardAttack.moveType(engine, battleKey); } - function isValidTarget(IEngine engine, bytes32 battleKey, uint240 extraData) external view returns (bool) { + function isValidTarget(IEngine engine, bytes32 battleKey, uint16 extraData) external view returns (bool) { return _standardAttack.isValidTarget(engine, battleKey, extraData); } diff --git a/test/mocks/EditEffectAttack.sol b/test/mocks/EditEffectAttack.sol index ebcffea0..670b8601 100644 --- a/test/mocks/EditEffectAttack.sol +++ b/test/mocks/EditEffectAttack.sol @@ -13,11 +13,12 @@ contract EditEffectAttack is IMoveSet { return "Edit Effect Attack"; } - function move(IEngine engine, bytes32, uint256, uint256, uint256, uint240 extraData, uint256) external { - // Unpack extraData: lower 80 bits = targetIndex, next 80 bits = monIndex, upper 80 bits = effectIndex - uint256 targetIndex = uint256(extraData) & ((1 << 80) - 1); - uint256 monIndex = (uint256(extraData) >> 80) & ((1 << 80) - 1); - uint256 effectIndex = (uint256(extraData) >> 160) & ((1 << 80) - 1); + function move(IEngine engine, bytes32, uint256, uint256, uint256, uint16 extraData, uint256) external { + // Unpack extraData (16 bits): bits 0..1 = targetIndex (0=p0, 1=p1, 2=global), + // bits 2..5 = monIndex (max 15), bits 6..15 = effectIndex (max 1023). + uint256 targetIndex = uint256(extraData) & 0x3; + uint256 monIndex = (uint256(extraData) >> 2) & 0xF; + uint256 effectIndex = (uint256(extraData) >> 6) & 0x3FF; engine.editEffect(targetIndex, monIndex, effectIndex, bytes32(uint256(69))); } @@ -33,7 +34,7 @@ contract EditEffectAttack is IMoveSet { return Type.Fire; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/EffectAttack.sol b/test/mocks/EffectAttack.sol index fe05a28d..dcd68cd5 100644 --- a/test/mocks/EffectAttack.sol +++ b/test/mocks/EffectAttack.sol @@ -33,7 +33,7 @@ contract EffectAttack is IMoveSet { return "Effect Attack"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint16, uint256) external { uint256 targetIndex = (attackerPlayerIndex + 1) % 2; engine.addEffect(targetIndex, defenderMonIndex, EFFECT, bytes32(0)); } @@ -50,7 +50,7 @@ contract EffectAttack is IMoveSet { return TYPE; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/ForceSwitchMove.sol b/test/mocks/ForceSwitchMove.sol index 6b83b8fb..d7966f40 100644 --- a/test/mocks/ForceSwitchMove.sol +++ b/test/mocks/ForceSwitchMove.sol @@ -30,10 +30,10 @@ contract ForceSwitchMove is IMoveSet { return "Force Switch"; } - function move(IEngine engine, bytes32, uint256, uint256, uint256, uint240 extraData, uint256) external { - // Decode data as packed (playerIndex in lower 120 bits, monToSwitchIndex in upper 120 bits) - uint256 playerIndex = uint256(extraData) & ((1 << 120) - 1); - uint256 monToSwitchIndex = uint256(extraData) >> 120; + function move(IEngine engine, bytes32, uint256, uint256, uint256, uint16 extraData, uint256) external { + // Pack: bit 0 = playerIndex (0 or 1), bits 1..15 = monToSwitchIndex + uint256 playerIndex = uint256(extraData) & 0x1; + uint256 monToSwitchIndex = uint256(extraData) >> 1; // Use the new switchActiveMon function engine.switchActiveMon(playerIndex, monToSwitchIndex); @@ -51,7 +51,7 @@ contract ForceSwitchMove is IMoveSet { return TYPE; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/GlobalEffectAttack.sol b/test/mocks/GlobalEffectAttack.sol index f5be9b13..b5bcb375 100644 --- a/test/mocks/GlobalEffectAttack.sol +++ b/test/mocks/GlobalEffectAttack.sol @@ -33,7 +33,7 @@ contract GlobalEffectAttack is IMoveSet { return "Effect Attack"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256, uint16, uint256) external { engine.addEffect(2, 0, EFFECT, bytes32(attackerPlayerIndex)); } @@ -49,7 +49,7 @@ contract GlobalEffectAttack is IMoveSet { return TYPE; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/InvalidMove.sol b/test/mocks/InvalidMove.sol index 3cee3926..dadb8746 100644 --- a/test/mocks/InvalidMove.sol +++ b/test/mocks/InvalidMove.sol @@ -15,7 +15,7 @@ contract InvalidMove is IMoveSet { return "Effect Attack"; } - function move(IEngine, bytes32, uint256, uint256, uint256, uint240, uint256) external pure { + function move(IEngine, bytes32, uint256, uint256, uint256, uint16, uint256) external pure { // No-op } @@ -31,7 +31,7 @@ contract InvalidMove is IMoveSet { return Type.Fire; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return false; } diff --git a/test/mocks/MockEffectRemover.sol b/test/mocks/MockEffectRemover.sol index df30442f..61802680 100644 --- a/test/mocks/MockEffectRemover.sol +++ b/test/mocks/MockEffectRemover.sol @@ -10,8 +10,7 @@ import {IMoveSet} from "../../src/moves/IMoveSet.sol"; /** * Mock move that removes an effect from a target mon. - * The effect address to remove is passed as extraData (targetArgs). - * Targets the opponent's active mon. + * The effect's slot index is passed as extraData. Targets the opponent's active mon. */ contract MockEffectRemover is IMoveSet { @@ -25,20 +24,23 @@ contract MockEffectRemover is IMoveSet { uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, - uint240 extraData, + uint16 extraData, uint256 ) external { - // extraData contains the address of the effect to remove (packed as uint160) - address effectToRemove = address(uint160(extraData)); + // extraData is the slot index of the effect to remove (from getEffects). + uint256 slotIndex = uint256(extraData); // Target the opponent's active mon uint256 targetPlayerIndex = 1 - attackerPlayerIndex; - // Find and remove the effect (removeEffect in Engine handles calling onRemove with proper params) - (EffectInstance[] memory effects, uint256[] memory indices) = engine.getEffects(battleKey, targetPlayerIndex, defenderMonIndex); - for (uint256 i = 0; i < effects.length; i++) { - if (address(effects[i].effect) == effectToRemove) { - engine.removeEffect(targetPlayerIndex, defenderMonIndex, indices[i]); + // Verify the slot is still occupied at this index before removing + (EffectInstance[] memory effects, uint256[] memory indices) = + engine.getEffects(battleKey, targetPlayerIndex, defenderMonIndex); + for (uint256 i = 0; i < indices.length; i++) { + if (indices[i] == slotIndex) { + if (address(effects[i].effect) != address(0)) { + engine.removeEffect(targetPlayerIndex, defenderMonIndex, slotIndex); + } break; } } @@ -60,7 +62,7 @@ contract MockEffectRemover is IMoveSet { return MoveClass.Other; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/MockKVWriterMove.sol b/test/mocks/MockKVWriterMove.sol index 79640190..22222733 100644 --- a/test/mocks/MockKVWriterMove.sol +++ b/test/mocks/MockKVWriterMove.sol @@ -9,17 +9,18 @@ import {IMoveSet} from "../../src/moves/IMoveSet.sol"; import {MoveMeta} from "../../src/Structs.sol"; /// @notice Test-only move that writes a single, caller-chosen (key, value) pair to globalKV. -/// @dev extraData layout: lower 64 bits = key, upper 176 bits = value. A 0-value input -/// writes 1 by default so tests can quickly assert "something was written" without -/// encoding a non-zero value every time. +/// @dev extraData layout (16 bits total): bits 0..9 = key (≤1023), bits 10..15 = value (≤63). +/// A 0-value input writes 1 by default so tests can quickly assert "something was written" +/// without encoding a non-zero value every time. Tests that need wider key/value ranges +/// should use a different mock. contract MockKVWriterMove is IMoveSet { function name() external pure returns (string memory) { return "MockKVWriter"; } - function move(IEngine engine, bytes32, uint256, uint256, uint256, uint240 extraData, uint256) external { - uint64 key = uint64(extraData); - uint192 value = uint192(uint256(extraData) >> 64); + function move(IEngine engine, bytes32, uint256, uint256, uint256, uint16 extraData, uint256) external { + uint64 key = uint64(extraData) & 0x3FF; // 10 bits + uint192 value = uint192(uint256(extraData) >> 10); // 6 bits if (value == 0) { value = 1; } @@ -42,7 +43,7 @@ contract MockKVWriterMove is IMoveSet { return MoveClass.Self; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/ReduceSpAtkMove.sol b/test/mocks/ReduceSpAtkMove.sol index 375e51dc..b5738980 100644 --- a/test/mocks/ReduceSpAtkMove.sol +++ b/test/mocks/ReduceSpAtkMove.sol @@ -20,7 +20,7 @@ contract ReduceSpAtkMove is IMoveSet { return "Reduce SpAtk"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint16, uint256) external { // Get the opposing player's index uint256 opposingPlayerIndex = (attackerPlayerIndex + 1) % 2; @@ -40,7 +40,7 @@ contract ReduceSpAtkMove is IMoveSet { return Type.Math; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/SelfSwitchAndDamageMove.sol b/test/mocks/SelfSwitchAndDamageMove.sol index 3fb5b1d1..ac3fff8b 100644 --- a/test/mocks/SelfSwitchAndDamageMove.sol +++ b/test/mocks/SelfSwitchAndDamageMove.sol @@ -21,7 +21,7 @@ contract SelfSwitchAndDamageMove is IMoveSet { return "Self Switch And Damage Move"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint240 extraData, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint16 extraData, uint256) external { uint256 monToSwitchIndex = uint256(extraData); // Deal damage first to opponent @@ -44,7 +44,7 @@ contract SelfSwitchAndDamageMove is IMoveSet { return Type.Fire; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/SkipTurnMove.sol b/test/mocks/SkipTurnMove.sol index be9db1e1..9120f4ae 100644 --- a/test/mocks/SkipTurnMove.sol +++ b/test/mocks/SkipTurnMove.sol @@ -30,7 +30,7 @@ contract SkipTurnMove is IMoveSet { return "Skip Turn"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint16, uint256) external { uint256 targetIndex = (attackerPlayerIndex + 1) % 2; engine.updateMonState(targetIndex, defenderMonIndex, MonStateIndexName.ShouldSkipTurn, 1); } @@ -47,7 +47,7 @@ contract SkipTurnMove is IMoveSet { return TYPE; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/StatBoostsMove.sol b/test/mocks/StatBoostsMove.sol index 2b869396..4abc7544 100644 --- a/test/mocks/StatBoostsMove.sol +++ b/test/mocks/StatBoostsMove.sol @@ -22,12 +22,12 @@ contract StatBoostsMove is IMoveSet { return ""; } - function move(IEngine engine, bytes32, uint256, uint256, uint256, uint240 extraData, uint256) external { - // Unpack extraData: lower 60 bits = playerIndex, next 60 bits = monIndex, next 60 bits = statIndex, upper 60 bits = boostAmount - uint256 playerIndex = uint256(extraData) & ((1 << 60) - 1); - uint256 monIndex = (uint256(extraData) >> 60) & ((1 << 60) - 1); - uint256 statIndex = (uint256(extraData) >> 120) & ((1 << 60) - 1); - int32 boostAmount = int32(int256((uint256(extraData) >> 180) & ((1 << 60) - 1))); + function move(IEngine engine, bytes32, uint256, uint256, uint256, uint16 extraData, uint256) external { + // Unpack extraData: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] + uint256 playerIndex = uint256(extraData) & 0x1; + uint256 monIndex = (uint256(extraData) >> 1) & 0x7; + uint256 statIndex = (uint256(extraData) >> 4) & 0xF; + int32 boostAmount = int32(int8(uint8(uint256(extraData) >> 8))); // For all tests, we'll use Temp stat boosts with Multiply type for positive boosts // and Divide type for negative boosts @@ -59,7 +59,7 @@ contract StatBoostsMove is IMoveSet { return Type.Air; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mocks/TestMoveFactory.sol b/test/mocks/TestMoveFactory.sol index 230d654f..02ab8f1d 100644 --- a/test/mocks/TestMoveFactory.sol +++ b/test/mocks/TestMoveFactory.sol @@ -24,7 +24,7 @@ contract TestMove is IMoveSet { return "Test Move"; } - function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint240, uint256) external { + function move(IEngine engine, bytes32, uint256 attackerPlayerIndex, uint256, uint256 defenderMonIndex, uint16, uint256) external { uint256 opponentIndex = (attackerPlayerIndex + 1) % 2; engine.dealDamage(opponentIndex, defenderMonIndex, _damage); } @@ -41,7 +41,7 @@ contract TestMove is IMoveSet { return _moveType; } - function isValidTarget(IEngine, bytes32, uint240) external pure returns (bool) { + function isValidTarget(IEngine, bytes32, uint16) external pure returns (bool) { return true; } diff --git a/test/mons/AuroxTest.sol b/test/mons/AuroxTest.sol index 89914532..bcd566fd 100644 --- a/test/mons/AuroxTest.sol +++ b/test/mons/AuroxTest.sol @@ -87,7 +87,7 @@ contract AuroxTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Bull Rush, Bob does nothing _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); @@ -177,7 +177,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice spends 1 stamina, Bob inflicts frostbite @@ -198,7 +198,7 @@ contract AuroxTest is Test, BattleHelper { ); // Alice swaps to mon index 1, Bob does the 50% attack - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 1, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 1, uint16(1), 0); // Verify that Alice's mon index 1 has taken 50% damage int32 aliceDamage = engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.Hp); @@ -209,7 +209,7 @@ contract AuroxTest is Test, BattleHelper { ); // Alice uses Gilded Recovery targeting self, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, uint16(1), 0); // Nothing should happen, mon index 0 for Alice should still have -1 staminaDelta, hpDelta for mon index 1 should still be the same assertEq( @@ -224,7 +224,7 @@ contract AuroxTest is Test, BattleHelper { ); // Alice uses Gilded Recovery targeting mon index , Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 2, NO_OP_MOVE_INDEX, uint16(0), 0); // Verify that Alice's mon index 1 is healed by 50% and mon index 0 has staminaDelta of 0, and no longer has frostbite assertEq( @@ -293,7 +293,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Iron Wall, Bob does nothing @@ -326,7 +326,7 @@ contract AuroxTest is Test, BattleHelper { assertEq(aliceDamage, expectedDamagePerHit * 2, "Alice's mon should take reduced damage on second hit"); // Alice switches to mon index 1, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0); // Verify that the Iron Wall effect is now gone after switch out (effects, ) = engine.getEffects(battleKey, 0, 0); @@ -380,7 +380,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Iron Wall, Bob does nothing @@ -443,7 +443,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice does nothing, Bob attacks @@ -518,7 +518,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice does nothing, Bob attacks @@ -613,7 +613,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses attack, Bob does nothing @@ -624,7 +624,7 @@ contract AuroxTest is Test, BattleHelper { assertEq(bobAttackDelta, int32(int8(upOnly.ATTACK_BOOST_PERCENT())) * int32(maxHp) / 100, "Bob's mon should be boosted"); // Alice does nothing, Bob switches to mon index 1 - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(1)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(1)); // Verify that Bob's mon index 0 has a positive attack delta of upOnly.ATTACK_BOOST_PERCENT() bobAttackDelta = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Attack); @@ -699,7 +699,7 @@ contract AuroxTest is Test, BattleHelper { // Turn 0: Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Turn 1: Alice does nothing, Bob attacks Alice @@ -760,7 +760,7 @@ contract AuroxTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Set rng to be 2 to trigger frostbite diff --git a/test/mons/EkinekiTest.sol b/test/mons/EkinekiTest.sol index 7a4315fd..9bcca4ab 100644 --- a/test/mons/EkinekiTest.sol +++ b/test/mons/EkinekiTest.sol @@ -121,7 +121,7 @@ contract EkinekiTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Bubble Bop, Bob does nothing @@ -147,7 +147,7 @@ contract EkinekiTest is Test, BattleHelper { bytes32 battleKey2 = _startBattle(validator2, engine2, mockOracle, registry2, matchmaker2, address(commitManager2)); _commitRevealExecuteForAliceAndBob( - engine2, commitManager2, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine2, commitManager2, battleKey2, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob uses single hit on Alice @@ -194,11 +194,11 @@ contract EkinekiTest is Test, BattleHelper { // Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses SneakAttack targeting Bob's non-active mon (index 1) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), 0); // Verify Bob's mon at index 1 (non-active) took damage int32 bobMon1HpDelta = engine.getMonStateForBattle(battleKey, 1, 1, MonStateIndexName.Hp); @@ -241,17 +241,17 @@ contract EkinekiTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses SneakAttack targeting Bob's non-active mon (index 1) - first use - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), 0); int32 bobMon1DamageAfterFirst = engine.getMonStateForBattle(battleKey, 1, 1, MonStateIndexName.Hp); assertTrue(bobMon1DamageAfterFirst < 0, "First sneak attack should deal damage"); // Alice uses SneakAttack again - should do nothing (already used this switch-in) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), 0); int32 bobMon1DamageAfterSecond = engine.getMonStateForBattle(battleKey, 1, 1, MonStateIndexName.Hp); assertEq( @@ -293,21 +293,21 @@ contract EkinekiTest is Test, BattleHelper { // Both players select mon 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses SneakAttack - first use works - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), 0); int32 damageAfterFirst = engine.getMonStateForBattle(battleKey, 1, 1, MonStateIndexName.Hp); assertTrue(damageAfterFirst < 0, "First sneak attack should deal damage"); // Alice switches to mon 1 (sneak attack effect removed on switch-out) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); // Alice (now mon 1) uses SneakAttack again - should work (reset by switch) - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), 0); int32 damageAfterReset = engine.getMonStateForBattle(battleKey, 1, 1, MonStateIndexName.Hp); assertTrue(damageAfterReset < damageAfterFirst, "Sneak attack should work again after switching"); } @@ -362,7 +362,7 @@ contract EkinekiTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob uses test attack without 999 (baseline) @@ -457,7 +457,7 @@ contract EkinekiTest is Test, BattleHelper { // Both select mon 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob attacks Alice's mon 0 (KO), Alice does nothing @@ -473,7 +473,7 @@ contract EkinekiTest is Test, BattleHelper { // Alice forced switch to mon 2 (the one with savior complex) // After KO, playerSwitchForTurnFlag = 0 (Alice must switch, no commit needed) vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(2), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(2), true); engine.resetCallContext(); // Verify that Alice's mon 2 got a sp atk boost (STAGE_1_BOOST = 15% of 100 = 15) int32 spAtkDelta = engine.getMonStateForBattle(battleKey, 0, 2, MonStateIndexName.SpecialAttack); @@ -556,7 +556,7 @@ contract EkinekiTest is Test, BattleHelper { // Both select mon 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob KOs Alice's mon 0 @@ -564,14 +564,14 @@ contract EkinekiTest is Test, BattleHelper { // Alice forced switch to mon 1 (savior complex triggers with 1 KO) vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), true); engine.resetCallContext(); int32 spAtkDeltaFirstSwitch = engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.SpecialAttack); assertEq(spAtkDeltaFirstSwitch, 15, "Should get 15 sp atk boost from 1 KO"); // Alice switches to mon 2, Bob KOs Alice's mon 2 in the same turn (Bob is faster but switch has higher priority) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(2), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(2), 0 ); // Verify Alice's mon 2 is KO'd @@ -583,7 +583,7 @@ contract EkinekiTest is Test, BattleHelper { // Alice forced switch back to mon 1 (savior complex should NOT trigger again) vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), true); engine.resetCallContext(); int32 spAtkDeltaSecondSwitch = engine.getMonStateForBattle(battleKey, 0, 1, MonStateIndexName.SpecialAttack); // Boost is temp so it was cleared when mon 1 switched out, and savior complex @@ -665,7 +665,7 @@ contract EkinekiTest is Test, BattleHelper { // Alice selects mon 0 (with savior complex) - no KO'd mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify no boost was applied (0 KOs) @@ -682,7 +682,7 @@ contract EkinekiTest is Test, BattleHelper { // Alice forced switch to mon 1 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(1), true); engine.resetCallContext(); // Mon 1 has no ability, so no savior complex trigger // But the savior complex on mon 0 should NOT have been consumed (it didn't trigger) @@ -720,7 +720,7 @@ contract EkinekiTest is Test, BattleHelper { _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Overflow, Bob does nothing diff --git a/test/mons/EmbursaTest.sol b/test/mons/EmbursaTest.sol index e80a42df..d5dae67c 100644 --- a/test/mons/EmbursaTest.sol +++ b/test/mons/EmbursaTest.sol @@ -94,12 +94,12 @@ contract EmbursaTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Q5, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Verify no damage occurred @@ -110,7 +110,7 @@ contract EmbursaTest is Test, BattleHelper { // Wait 4 turns for (uint256 i = 0; i < 4; i++) { _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); } // Verify no damage occurred @@ -123,7 +123,7 @@ contract EmbursaTest is Test, BattleHelper { // Alice and Bob both do nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Verify damage occurred @@ -226,7 +226,7 @@ contract EmbursaTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); (EffectInstance[] memory effects, ) = engine.getEffects(battleKey, 1, 0); @@ -259,7 +259,7 @@ contract EmbursaTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); (effects, ) = engine.getEffects(battleKey, 1, 0); assertEq(effects.length, 0, "Bob's mon should have no effects"); @@ -276,7 +276,7 @@ contract EmbursaTest is Test, BattleHelper { // Verify Q5 was applied to global effects, verify Alice is KOed battleKey = _startBattle(validatorToUse, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, 4, 0, 0); @@ -295,7 +295,7 @@ contract EmbursaTest is Test, BattleHelper { // Verify Honey Bribe applied stat boost to Bob's mon, verify Alice is KOed battleKey = _startBattle(validatorToUse, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 3, 4, 0, 0); @@ -372,7 +372,7 @@ contract EmbursaTest is Test, BattleHelper { // Both switch in _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Q5, Bob does nothing @@ -381,7 +381,7 @@ contract EmbursaTest is Test, BattleHelper { // Wait 4 turns (Q5 counter ticks from 1 to 5) for (uint256 i = 0; i < 4; i++) { _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); } @@ -469,7 +469,7 @@ contract EmbursaTest is Test, BattleHelper { vm.warp(vm.getBlockTimestamp() + 1); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Set RNG so that burn triggers (rng % 3 == 2) @@ -555,7 +555,7 @@ contract EmbursaTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validatorToUse, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob uses burn attack on Alice (Bob is faster) diff --git a/test/mons/GhouliathTest.sol b/test/mons/GhouliathTest.sol index 2a12be8d..c94c1ce6 100644 --- a/test/mons/GhouliathTest.sol +++ b/test/mons/GhouliathTest.sol @@ -150,7 +150,7 @@ contract GhouliathTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob uses the attack (which KOs) on Alice's mon @@ -170,7 +170,7 @@ contract GhouliathTest is Test, BattleHelper { // Alice swaps in mon index 1 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // We wait for the REVIVAL_DELAY - 1 turns to pass for (uint256 i = 0; i < riseFromTheGrave.REVIVAL_DELAY() - 1; i++) { @@ -188,12 +188,12 @@ contract GhouliathTest is Test, BattleHelper { assertEq(damageTaken, -99, "Alice's mon should have 1 HP"); // Alice swaps in mon index 0, Bob does attack again, which KOs Alice's mon - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, 0, uint16(0), 0); // Verify the mon is not revived after REVIVAL_DELAY turns // (First we swap in mon index 1) vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); for (uint256 i = 0; i < riseFromTheGrave.REVIVAL_DELAY() - 1; i++) { _commitRevealExecuteForAliceAndBob( @@ -256,7 +256,7 @@ contract GhouliathTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob uses the attack (which KOs) on Alice's mon @@ -264,14 +264,14 @@ contract GhouliathTest is Test, BattleHelper { // Alice swaps in mon index 1 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice KOs Bob's mon _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, 0, 0); // Bob swaps in mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // We wait for the REVIVAL_DELAY turns to pass for (uint256 i = 0; i < riseFromTheGrave.REVIVAL_DELAY() - 1; i++) { @@ -346,7 +346,7 @@ contract GhouliathTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses WitherAway on Bob's mon @@ -448,7 +448,7 @@ contract GhouliathTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Osteoporosis on Bob's mon @@ -500,12 +500,12 @@ contract GhouliathTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice does nothing, Bob switches to mon index 1 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(1) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(1) ); // Alice uses Eternal Grudge on Bob's mon diff --git a/test/mons/GorillaxTest.sol b/test/mons/GorillaxTest.sol index 8373d33d..9093a41f 100644 --- a/test/mons/GorillaxTest.sol +++ b/test/mons/GorillaxTest.sol @@ -97,7 +97,7 @@ contract GorillaxTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice chooses to attack, Bob chooses to do nothing for CHARGE_COUNT rounds @@ -168,12 +168,12 @@ contract GorillaxTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Rock Pull, Bob switches to mon index 1 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, uint240(0), uint240(1) + engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, uint16(0), uint16(1) ); // Assert that Bob's mon index 0 took damage @@ -184,7 +184,7 @@ contract GorillaxTest is Test, BattleHelper { // Alice uses Rock Pull, Bob does not switch _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Assert that Alice's mon index 0 took damage diff --git a/test/mons/IblivionTest.sol b/test/mons/IblivionTest.sol index 56fcb7d9..e8bd5659 100644 --- a/test/mons/IblivionTest.sol +++ b/test/mons/IblivionTest.sol @@ -112,7 +112,7 @@ contract IblivionTest is Test, BattleHelper { // Switch in mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Check that Baselight level is 2 (1 from switch-in + 1 from round end) @@ -153,7 +153,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Baselight starts at 2 (1 from switch + 1 from round end) @@ -227,7 +227,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice starts with 2 Baselight stacks (1 from switch + 1 from round end) @@ -292,7 +292,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // No Baselight stacks @@ -370,7 +370,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice has 2 stacks (not 3), so normal power (80) @@ -441,7 +441,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Wait for 3 stacks (start at 2, need 1 more round) @@ -500,7 +500,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // At Baselight 2, Loop should give 30% boost @@ -555,7 +555,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Loop first time @@ -611,7 +611,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Wait for 3 stacks (start at 2, need 1 more round) @@ -666,7 +666,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Start at 2 stacks (1 initial + 1 from round end) @@ -714,7 +714,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Loop to get stat boosts @@ -767,7 +767,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Loop @@ -847,7 +847,7 @@ contract IblivionTest is Test, BattleHelper { bytes32 battleKey = _startBattle(validator, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Check priority @@ -945,7 +945,7 @@ contract IblivionTest is Test, BattleHelper { // Switch in mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify Alice's attack starts at base (0 delta) @@ -994,10 +994,20 @@ contract IblivionTest is Test, BattleHelper { } assertTrue(stillHasBurn, "Alice should still have burn effect after Renormalize"); - // Bob uses MockEffectRemover to remove burn from Alice (move index 1) - // Pass burn status address as extraData + // Bob uses MockEffectRemover to remove burn from Alice (move index 1). + // Pass burn status's slot index as extraData (looked up from Alice's effects). + uint16 burnSlot; + { + (EffectInstance[] memory beforeRemove, uint256[] memory beforeIndices) = engine.getEffects(battleKey, 0, 0); + for (uint256 i = 0; i < beforeRemove.length; i++) { + if (address(beforeRemove[i].effect) == address(burnStatus)) { + burnSlot = uint16(beforeIndices[i]); + break; + } + } + } _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 1, 0, uint240(uint160(address(burnStatus))) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 1, 0, burnSlot ); // Verify burn effect is removed diff --git a/test/mons/InutiaTest.sol b/test/mons/InutiaTest.sol index 57434918..8c1f37a0 100644 --- a/test/mons/InutiaTest.sol +++ b/test/mons/InutiaTest.sol @@ -112,7 +112,7 @@ contract InutiaTest is Test, BattleHelper { // First move: Alice switches to the mon with Interweaving ability, Bob switches to the other mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(1), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(1), uint16(0) ); // Check that Bob's mon Attack stat has been decreased @@ -124,7 +124,7 @@ contract InutiaTest is Test, BattleHelper { // Alice switches back to the regular mon, Bob does a No-Op _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); // Check that Bob's mon SpecialAttack stat has been decreased @@ -177,7 +177,7 @@ contract InutiaTest is Test, BattleHelper { // Send in mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both players select move index 0 @@ -205,7 +205,7 @@ contract InutiaTest is Test, BattleHelper { // Now both players swap to mon index 1 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(1), uint240(1) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(1), uint16(1) ); // The stat boost should carry over @@ -220,7 +220,7 @@ contract InutiaTest is Test, BattleHelper { // Now both players swap back to mon index 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both players select move index 0 @@ -326,7 +326,7 @@ contract InutiaTest is Test, BattleHelper { bytes32 battleKey = _startBattle(v, engine, mockOracle, defaultRegistry, matchmaker, address(commitManager)); _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses CE, Bob does nothing (turn 1) @@ -339,7 +339,7 @@ contract InutiaTest is Test, BattleHelper { // Bob swaps to mon index 1 (turn 3) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(1) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(1) ); // Verify damage dealt to Bob's mon index 1 is 1/16 of max HP (charge 1) @@ -348,7 +348,7 @@ contract InutiaTest is Test, BattleHelper { // Bob swaps to mon index 2 (turn 4) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(2) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(2) ); // Verify damage dealt to Bob's mon index 2 is 1/4 of max HP (charge 2) @@ -357,7 +357,7 @@ contract InutiaTest is Test, BattleHelper { // Bob swaps back to mon index 0 (turn 5) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(0) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(0) ); // Verify damage dealt to Bob's mon index 0 is 1/8 of max HP (charge 3) @@ -366,7 +366,7 @@ contract InutiaTest is Test, BattleHelper { // Bob swaps to mon index 1 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint240(1) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, 0, uint16(1) ); // Verify damage dealt to Bob's mon index 1 is 1/16 of max HP (charge 4) @@ -391,12 +391,12 @@ contract InutiaTest is Test, BattleHelper { // Alice swaps to mon index 1, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); // Alice swaps back to mon index 0, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); // Verify Alice's mon index 0 is healed by 1/16 of max HP diff --git a/test/mons/MalalienTest.sol b/test/mons/MalalienTest.sol index 63557fbb..569b76d6 100644 --- a/test/mons/MalalienTest.sol +++ b/test/mons/MalalienTest.sol @@ -129,7 +129,7 @@ contract MalalienTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice's mon has ActusReus ability @@ -153,7 +153,7 @@ contract MalalienTest is Test, BattleHelper { // Bob switches to mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice does nothing, Bob attacks and KOs Alice's mon _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, NO_OP_MOVE_INDEX, 0, 0, 0); @@ -208,11 +208,11 @@ contract MalalienTest is Test, BattleHelper { // Alice and Bob both send in mon index 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both players use triple think - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 0, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 0, uint16(0), uint16(0)); // SpecialAttack delta for both is 100 int32 aliceSpAtkBoost = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.SpecialAttack); @@ -223,7 +223,7 @@ contract MalalienTest is Test, BattleHelper { // Alice uses it again, Bob swaps out _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, uint240(0), uint240(1) + engine, commitManager, battleKey, 0, SWITCH_MOVE_INDEX, uint16(0), uint16(1) ); // Alice should be at 1.75 * 1.75 = 1.225 + 1.75 + 0.0875 = 2.0625, Bob should be at 0 diff --git a/test/mons/PengymTest.sol b/test/mons/PengymTest.sol index 6346bd03..8c0e75c0 100644 --- a/test/mons/PengymTest.sol +++ b/test/mons/PengymTest.sol @@ -180,7 +180,7 @@ contract PengymTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Check that Alice's mon has the PostWorkout effect @@ -216,12 +216,12 @@ contract PengymTest is Test, BattleHelper { // Alice switches to her second mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); // Alice switches back to her first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); // Check that Alice's mon no longer has the PanicStatus effect @@ -355,7 +355,7 @@ contract PengymTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Check that Alice's mon has the PostWorkout effect @@ -396,12 +396,12 @@ contract PengymTest is Test, BattleHelper { // Alice switches to her second mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); // Alice switches back to her first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); // Check that Alice's mon no longer has the FrostbiteStatus effect @@ -472,25 +472,25 @@ contract PengymTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice deals damage to Bob, record the damage dealt _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); int32 deepFreezeDamage = -1 * engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); // Alice inflicts frostbite on Bob, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); int32 bobDamageBefore = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); // Alice uses deep freeze, record the damage dealt _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); int32 bobDamageAfter = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); @@ -576,11 +576,11 @@ contract PengymTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice selects pistol squat, Bob selects move index 1 and outspeeds - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 1, uint240(0), uint240(0)); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, 1, uint16(0), uint16(0)); // Alice should be KO'ed int32 koFlag = engine.getMonStateForBattle(battleKey, 0, 0, MonStateIndexName.IsKnockedOut); @@ -588,11 +588,11 @@ contract PengymTest is Test, BattleHelper { // Alice swaps to mon index 1 vm.startPrank(ALICE); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice selects pistol squat, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Active mon for Bob should be 1 @@ -601,7 +601,7 @@ contract PengymTest is Test, BattleHelper { // Alice selects pistol squat, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Active mon for Bob should be 0 @@ -610,7 +610,7 @@ contract PengymTest is Test, BattleHelper { // Alice selects pistol squat, Bob does nothing (and dies) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); bobActiveMonIndex = engine.getActiveMonIndexForBattleState(battleKey)[1]; @@ -618,21 +618,21 @@ contract PengymTest is Test, BattleHelper { // Bob sends in mon index 1 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(1), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(1), true); engine.resetCallContext(); // Alice selects pistol squat, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Now Bob's mon index 1 is KOed // Bob sends in mon index 2 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint240(2), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, 0, uint16(2), true); engine.resetCallContext(); // Alice selects pistol squat, Bob does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Now Bob has mon index 2 (already took damage) and mon index 3 @@ -641,21 +641,21 @@ contract PengymTest is Test, BattleHelper { // Bob switches back to mon index 2, Alice does nothing _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(2) + engine, commitManager, battleKey, NO_OP_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(2) ); // Alice KOs Bob's mon index 2 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); // Bob sends in mon index 3 vm.startPrank(BOB); - commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, "", uint240(3), true); + commitManager.revealMove(battleKey, SWITCH_MOVE_INDEX, uint104(0), uint16(3), true); engine.resetCallContext(); // Alice tries to force a switch, but active mon should not change _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), uint16(0) ); bobActiveMonIndex = engine.getActiveMonIndexForBattleState(battleKey)[1]; assertEq(bobActiveMonIndex, 3, "No mons left"); diff --git a/test/mons/SofabbiTest.sol b/test/mons/SofabbiTest.sol index ab64d93d..0221faf7 100644 --- a/test/mons/SofabbiTest.sol +++ b/test/mons/SofabbiTest.sol @@ -108,7 +108,7 @@ contract SofabbiTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify that the CarrotHarvest effect was applied to Alice's mon @@ -117,12 +117,12 @@ contract SofabbiTest is Test, BattleHelper { // Now have Alice switch to her second mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0 ); // Now have Alice switch back to her first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0 + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0 ); // Verify that the CarrotHarvest effect is still only applied once @@ -188,7 +188,7 @@ contract SofabbiTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify that staminaDelta is 1 for both mons @@ -297,28 +297,28 @@ contract SofabbiTest is Test, BattleHelper { // First move: Both players select their first mon (index 0, the Air mon) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Damage is returned in negative, so that's why there are some weird sign cancellations below // Alice uses Guest Feature targeting mon index 1, it should deal 2x damage _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(1), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(1), uint16(0) ); int32 bobDmg = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); assertApproxEqRel(-1 * bobDmg, int32(2 * gf.BASE_POWER()), 2e17); // Alice uses Guest Feature targeting mon index 2, it should deal 0 damage _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(2), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(2), uint16(0) ); int32 newBobDmg = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); assertEq(newBobDmg, bobDmg, "No damage"); // Alice uses Guest Feature targeting mon index 3, it should deal 1/2 damage _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(3), uint240(0) + engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(3), uint16(0) ); newBobDmg = engine.getMonStateForBattle(battleKey, 1, 0, MonStateIndexName.Hp); bobDmg = bobDmg - newBobDmg; @@ -377,7 +377,7 @@ contract SofabbiTest is Test, BattleHelper { // Both players send in mon index 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses nothing, Bob uses move index 1, puts Alice at 1 HP @@ -454,7 +454,7 @@ contract SofabbiTest is Test, BattleHelper { // Both players send in mon index 0 _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Set rng to be 10 diff --git a/test/mons/VolthareTest.sol b/test/mons/VolthareTest.sol index 7991286b..3f222e21 100644 --- a/test/mons/VolthareTest.sol +++ b/test/mons/VolthareTest.sol @@ -124,7 +124,7 @@ contract VolthareTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Verify that Bob's mon took damage from PreemptiveShock @@ -207,11 +207,11 @@ contract VolthareTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses the Overclock move (move index 0), Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 0, NO_OP_MOVE_INDEX, uint16(0), 0); // Verify that Overclock is applied (EffectInstance[] memory effects,) = engine.getEffects(battleKey, 2, 0); @@ -222,7 +222,7 @@ contract VolthareTest is Test, BattleHelper { mockOracle.setRNG(2); // Alice uses Mega Star Blast (move index 1), Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), 0); // Verify that Bob's mon is zapped (effects,) = engine.getEffects(battleKey, 1, 0); @@ -237,7 +237,7 @@ contract VolthareTest is Test, BattleHelper { mockOracle.setRNG(msb.BASE_ACCURACY() + 1); // Alice uses Mega Star Blast, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), 0); // Verify that Bob's mon is not zapped (again) (effects,) = engine.getEffects(battleKey, 1, 0); @@ -303,7 +303,7 @@ contract VolthareTest is Test, BattleHelper { // Both players send in their mons _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Bob applies Overclock (stored with player index 1). Alice does nothing. @@ -320,7 +320,7 @@ contract VolthareTest is Test, BattleHelper { mockOracle.setRNG(msb.BASE_ACCURACY() + 1); // Alice uses MegaStarBlast. Bob does nothing. - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, uint16(0), 0); // Overclock should still be present (Alice cannot clear Bob's Overclock) (effects,) = engine.getEffects(battleKey, 2, 0); @@ -396,7 +396,7 @@ contract VolthareTest is Test, BattleHelper { // First move: Both players select their first mon (index 0) _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Both players use Dual Shock, Alice should move first and skip their next move diff --git a/test/mons/XmonTest.sol b/test/mons/XmonTest.sol index 10b041b6..8c3d995d 100644 --- a/test/mons/XmonTest.sol +++ b/test/mons/XmonTest.sol @@ -87,7 +87,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Contagious Slumber, Bob does nothing @@ -160,7 +160,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Set RNG to guarantee stamina steal (>= 50) @@ -219,7 +219,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Alice uses Somniphobia, Bob uses Somniphobia too @@ -337,7 +337,7 @@ contract XmonTest is Test, BattleHelper { // Alice sends in fast mon, Bob sends in slow mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(1) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(1) ); // Verify that Alice has the Dreamcatcher effect @@ -400,7 +400,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Turn 1: Alice uses Night Terrors, Bob does nothing @@ -467,7 +467,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Turn 1: Alice uses Night Terrors, Bob does nothing @@ -485,7 +485,7 @@ contract XmonTest is Test, BattleHelper { assertTrue(hasNightTerrorsBeforeSwap, "Alice's mon 0 should have Night Terrors effect before swap"); // Turn 2: Alice swaps to mon 1, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0); // Verify Alice's mon 0 no longer has Night Terrors effect (EffectInstance[] memory aliceEffectsAfterSwap, ) = engine.getEffects(battleKey, 0, 0); @@ -555,7 +555,7 @@ contract XmonTest is Test, BattleHelper { // Both players select their first mon _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Set RNG to 1 to prevent early waking from sleep (rng % 3 == 0 wakes early) @@ -569,10 +569,10 @@ contract XmonTest is Test, BattleHelper { int32 awakeDamage = -bobHpAfterAwakeDamage; // Turn 2: Alice swaps out to mon 1 to clear Night Terrors, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(1), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(1), 0); // Turn 3: Alice swaps back to mon 0, Bob does nothing - _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint240(0), 0); + _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, SWITCH_MOVE_INDEX, NO_OP_MOVE_INDEX, uint16(0), 0); // Turn 4: Alice uses Sleep move on Bob, Bob does nothing _commitRevealExecuteForAliceAndBob(engine, commitManager, battleKey, 1, NO_OP_MOVE_INDEX, 0, 0); diff --git a/test/moves/StandardAttackRngTest.sol b/test/moves/StandardAttackRngTest.sol index d06be33b..836976cc 100644 --- a/test/moves/StandardAttackRngTest.sol +++ b/test/moves/StandardAttackRngTest.sol @@ -93,7 +93,7 @@ contract StandardAttackRngTest is Test, BattleHelper { // Switch in mon 0 on both sides. _commitRevealExecuteForAliceAndBob( - engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint240(0), uint240(0) + engine, commitManager, battleKey, SWITCH_MOVE_INDEX, SWITCH_MOVE_INDEX, uint16(0), uint16(0) ); // Oracle returns the same rng for both attackers this turn. From bb3c4f73ff1e49e89a0837b26639bbcdb222a5cd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Apr 2026 16:48:48 +0000 Subject: [PATCH 2/2] Phase 0 cleanup: dedupe test helpers, trim narrative comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per /simplify review of Phase 0: 1. **Consolidate EIP-712 signing helpers.** Phase 0 introduced four byte-identical copies of `_signCommit` and grew `_signDualReveal` to four copies (it had two pre-Phase 0). Added `test/abstract/SignedCommitHelper.sol` — a standalone abstract that takes the verifying-contract address as a parameter (no `EIP712` inheritance needed; `_DOMAIN_TYPEHASH` is replicated as a constant) and is inherited by `SignedCommitManager.t.sol`, `BatchInstrumentationTest`, `InlineEngineGasTest::FullyOptimizedInlineGasTest`, and `StandardAttackPvPGasTest`. Drops ~280 lines and the `_domainNameAndVersion` override boilerplate from each. 2. **Move `_packStatBoost` to `BattleHelper`.** Three byte-identical copies (load-bearing — the bit layout must stay in lockstep with `StatBoostsMove`'s decoder) collapsed into a single home. 3. **Use existing `_createMonWithSpeed` helper in `BetterCPUTest.sol`.** The two Phase 0 edits open-coded `_createMon(...); mon.stats.speed = 100;` — replaced with the helper that already exists at line 77. 4. **Trim narrative `// what` comments from `SignedCommitManager.sol` per CLAUDE.md guidance** (default to no comments; only WHY). Kept the stack-pressure rationale on the scoped recovery blocks and the natdoc on the function itself (which carries the real WHY about the unilateral-revealer attack). 5. **Add a one-line WHY for the salt truncation in `CPUMoveManager.sol`** so the deliberate 256→104-bit narrowing isn't cargo-culted as accidental. Build clean. `forge test`: 352/352 passing. Gas snapshots refreshed where the helper-extraction path shifted dispatch overhead. Skipped findings (deliberate): - Hoisting `_getCurrentTurnSalt` calls in `Engine.sol` to save one or two transient loads per turn — pre-existing inefficiency, scope of the later helper-extraction phase (§9 Phase 0.5). - Tightening `MockKVWriterMove`'s misleading `uint192 value` to a true 6-bit type with bound checks — works for current tests, value-range guard would be useful but the mock is test-only. https://claude.ai/code/session_01Lc98i85bMi3SWTBnaNiDuZ --- snapshots/BetterCPUInlineGasTest.json | 12 +- snapshots/EngineOptimizationTest.json | 2 +- snapshots/FullyOptimizedInlineGasTest.json | 12 +- snapshots/InlineEngineGasTest.json | 14 +- snapshots/StandardAttackPvPGasTest.json | 10 +- src/commit-manager/SignedCommitManager.sol | 9 +- src/cpu/CPUMoveManager.sol | 2 + test/BatchInstrumentationTest.sol | 68 +-------- test/BetterCPUTest.sol | 12 +- test/EngineGasTest.sol | 10 -- test/InlineEngineGasTest.sol | 88 +---------- test/SignedCommitManager.t.sol | 170 ++++++--------------- test/SignedCommitManagerGasBenchmark.t.sol | 16 +- test/StandardAttackPvPGasTest.sol | 68 +-------- test/abstract/BattleHelper.sol | 15 ++ test/abstract/SignedCommitHelper.sol | 72 +++++++++ test/effects/StatBoosts.t.sol | 10 -- 17 files changed, 186 insertions(+), 404 deletions(-) create mode 100644 test/abstract/SignedCommitHelper.sol diff --git a/snapshots/BetterCPUInlineGasTest.json b/snapshots/BetterCPUInlineGasTest.json index 36bb56f3..b2978ec6 100644 --- a/snapshots/BetterCPUInlineGasTest.json +++ b/snapshots/BetterCPUInlineGasTest.json @@ -1,8 +1,8 @@ { - "Flag0_P0ForcedSwitch": "22814", - "Turn0_Lead": "104286", - "Turn1_BothAttack": "264139", - "Turn2_BothAttack": "238215", - "Turn3_BothAttack": "234239", - "Turn4_BothAttack": "234243" + "Flag0_P0ForcedSwitch": "22763", + "Turn0_Lead": "104184", + "Turn1_BothAttack": "264088", + "Turn2_BothAttack": "238164", + "Turn3_BothAttack": "234188", + "Turn4_BothAttack": "234192" } \ No newline at end of file diff --git a/snapshots/EngineOptimizationTest.json b/snapshots/EngineOptimizationTest.json index 68dfd453..09b37c45 100644 --- a/snapshots/EngineOptimizationTest.json +++ b/snapshots/EngineOptimizationTest.json @@ -1,4 +1,4 @@ { "ExternalStaminaRegen": "392424", - "InlineStaminaRegen": "999137" + "InlineStaminaRegen": "999051" } \ No newline at end of file diff --git a/snapshots/FullyOptimizedInlineGasTest.json b/snapshots/FullyOptimizedInlineGasTest.json index 7efaad27..66ef6ab8 100644 --- a/snapshots/FullyOptimizedInlineGasTest.json +++ b/snapshots/FullyOptimizedInlineGasTest.json @@ -1,8 +1,8 @@ { - "Fast_Battle1": "1867773", - "Fast_Battle2": "1778380", - "Fast_Battle3": "1288990", - "Fast_Setup_1": "1322085", - "Fast_Setup_2": "216727", - "Fast_Setup_3": "213092" + "Fast_Battle1": "1867564", + "Fast_Battle2": "1777864", + "Fast_Battle3": "1288656", + "Fast_Setup_1": "1322088", + "Fast_Setup_2": "216706", + "Fast_Setup_3": "212909" } \ No newline at end of file diff --git a/snapshots/InlineEngineGasTest.json b/snapshots/InlineEngineGasTest.json index 5dec063e..d4833e98 100644 --- a/snapshots/InlineEngineGasTest.json +++ b/snapshots/InlineEngineGasTest.json @@ -1,16 +1,16 @@ { - "B1_Execute": "870187", + "B1_Execute": "870101", "B1_Setup": "758557", - "B2_Execute": "606943", + "B2_Execute": "606857", "B2_Setup": "271758", - "Battle1_Execute": "401332", + "Battle1_Execute": "401230", "Battle1_Setup": "733775", - "Battle2_Execute": "319934", + "Battle2_Execute": "319832", "Battle2_Setup": "224511", - "FirstBattle": "2596071", - "SecondBattle": "2583719", + "FirstBattle": "2595770", + "SecondBattle": "2583418", "Setup 1": "1612191", "Setup 2": "318239", "Setup 3": "314704", - "ThirdBattle": "1967796" + "ThirdBattle": "1967495" } \ No newline at end of file diff --git a/snapshots/StandardAttackPvPGasTest.json b/snapshots/StandardAttackPvPGasTest.json index 3554d323..8c303306 100644 --- a/snapshots/StandardAttackPvPGasTest.json +++ b/snapshots/StandardAttackPvPGasTest.json @@ -1,7 +1,7 @@ { - "Turn0_Lead": "71464", - "Turn1_BothAttack": "123619", - "Turn2_BothAttack": "83843", - "Turn3_BothAttack": "83869", - "Turn4_BothAttack": "83898" + "Turn0_Lead": "71332", + "Turn1_BothAttack": "123589", + "Turn2_BothAttack": "83813", + "Turn3_BothAttack": "83839", + "Turn4_BothAttack": "83868" } \ No newline at end of file diff --git a/src/commit-manager/SignedCommitManager.sol b/src/commit-manager/SignedCommitManager.sol index d12a471e..aa0831fa 100644 --- a/src/commit-manager/SignedCommitManager.sol +++ b/src/commit-manager/SignedCommitManager.sol @@ -82,13 +82,11 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { bytes calldata committerSignature, bytes calldata revealerSignature ) external { - // Use lightweight getter (validates internally, reverts on bad state) (address committer, address revealer, uint64 turnId) = ENGINE.getCommitAuthForDualSigned(battleKey); - // Compute the committer's move hash bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); - // Verify the committer's signature over SignedCommit + // Scoped to keep `commit`/`reveal` structs from sharing stack space across recoveries. { SignedCommitLib.SignedCommit memory commit = SignedCommitLib.SignedCommit({ moveHash: committerMoveHash, @@ -101,7 +99,6 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { } } - // Verify the revealer's signature over DualSignedReveal { SignedCommitLib.DualSignedReveal memory reveal = SignedCommitLib.DualSignedReveal({ battleKey: battleKey, @@ -117,10 +114,7 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { } } - // Execute with moves in a single call (engine validates during execution) - // No playerData updates needed - engine tracks lastExecuteTimestamp for timeouts if (turnId % 2 == 0) { - // Committer is p0 ENGINE.executeWithMoves( battleKey, committerMoveIndex, @@ -131,7 +125,6 @@ contract SignedCommitManager is DefaultCommitManager, EIP712 { revealerExtraData ); } else { - // Committer is p1 ENGINE.executeWithMoves( battleKey, revealerMoveIndex, diff --git a/src/cpu/CPUMoveManager.sol b/src/cpu/CPUMoveManager.sol index 9dcba344..1f286113 100644 --- a/src/cpu/CPUMoveManager.sol +++ b/src/cpu/CPUMoveManager.sol @@ -45,6 +45,8 @@ abstract contract CPUMoveManager { // P1's turn or both players move: CPU calculates its move. Fetch the full context now. CPUContext memory ctx = ENGINE.getCPUContext(battleKey); (uint128 cpuMoveIndex, uint16 cpuExtraData) = ICPU(address(this)).calculateMove(ctx, moveIndex, extraData); + // Salt narrows to 104 bits to match the engine's storage; ample for an unpredictable + // RNG source within the seconds-to-minutes commit-reveal window. uint104 p1Salt = uint104(uint256(keccak256(abi.encode(battleKey, msg.sender, block.timestamp)))); if (playerSwitchForTurnFlag == 1) { diff --git a/test/BatchInstrumentationTest.sol b/test/BatchInstrumentationTest.sol index bbd26dbb..30afdfba 100644 --- a/test/BatchInstrumentationTest.sol +++ b/test/BatchInstrumentationTest.sol @@ -13,7 +13,7 @@ import {SignedMatchmaker} from "../src/matchmaker/SignedMatchmaker.sol"; import {BattleOfferLib} from "../src/matchmaker/BattleOfferLib.sol"; import {StandardAttackFactory} from "../src/moves/StandardAttackFactory.sol"; import {ATTACK_PARAMS} from "../src/moves/StandardAttackStructs.sol"; -import {EIP712} from "../src/lib/EIP712.sol"; +import {SignedCommitHelper} from "./abstract/SignedCommitHelper.sol"; import {IEngine} from "../src/IEngine.sol"; import {IEngineHook} from "../src/IEngineHook.sol"; @@ -30,7 +30,7 @@ import {TestTeamRegistry} from "./mocks/TestTeamRegistry.sol"; /// Counts SLOAD / SSTORE access patterns on a warm steady-state turn, to ground the PLAN_OPT.md /// gas math in real data instead of estimates. -contract BatchInstrumentationTest is Test, EIP712 { +contract BatchInstrumentationTest is SignedCommitHelper { uint256 constant MONS_PER_TEAM = 4; uint256 constant MOVES_PER_MON = 4; @@ -47,10 +47,6 @@ contract BatchInstrumentationTest is Test, EIP712 { TestTeamRegistry defaultRegistry; StandardAttackFactory attackFactory; - function _domainNameAndVersion() internal pure override returns (string memory, string memory) { - return ("SignedCommitManager", "1"); - } - function setUp() public { p0 = vm.addr(P0_PK); p1 = vm.addr(P1_PK); @@ -103,61 +99,6 @@ contract BatchInstrumentationTest is Test, EIP712 { return battleKey; } - function _signDualReveal( - uint256 privateKey, - bytes32 battleKey, - uint64 turnId, - bytes32 committerMoveHash, - uint8 revealerMoveIndex, - uint104 revealerSalt, - uint16 revealerExtraData - ) internal view returns (bytes memory) { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashDualSignedReveal( - SignedCommitLib.DualSignedReveal({ - battleKey: battleKey, - turnId: turnId, - committerMoveHash: committerMoveHash, - revealerMoveIndex: revealerMoveIndex, - revealerSalt: revealerSalt, - revealerExtraData: revealerExtraData - }) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - - function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) - internal - view - returns (bytes memory) - { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashSignedCommit( - SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - function _fastTurn( bytes32 battleKey, uint8 p0MoveIndex, @@ -197,9 +138,10 @@ contract BatchInstrumentationTest is Test, EIP712 { bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); - bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); + address mgr = address(signedCommitManager); + bytes memory committerSig = _signCommit(mgr, committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( - revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData + mgr, revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); vm.prank(committer); diff --git a/test/BetterCPUTest.sol b/test/BetterCPUTest.sol index f825a424..5965ae46 100644 --- a/test/BetterCPUTest.sol +++ b/test/BetterCPUTest.sol @@ -436,13 +436,11 @@ contract BetterCPUTest is Test { // CPU mon is faster than Alice's so attack ordering is deterministic regardless // of the engine's RNG-based speed-tie breaker. - Mon memory aliceMon = _createMon(Type.Fire, 100, 50, 10); + Mon memory aliceMon = _createMonWithSpeed(Type.Fire, 100, 50, 10, 10); aliceMon.moves = moves; - Mon memory cpuMon = _createMon(Type.Fire, 100, 50, 10); - cpuMon.stats.speed = 100; + Mon memory cpuMon = _createMonWithSpeed(Type.Fire, 100, 50, 10, 100); cpuMon.moves = moves; - // Need 2 mons per team Mon[] memory aliceTeam = new Mon[](2); aliceTeam[0] = aliceMon; aliceTeam[1] = aliceMon; @@ -1066,11 +1064,9 @@ contract BetterCPUTest is Test { // CPU mons are faster so they attack first and get to deal damage before being KO'd. Mon[] memory cpuTeam = new Mon[](2); - cpuTeam[0] = _createMon(Type.Fire, 100, 50, 10); - cpuTeam[0].stats.speed = 100; + cpuTeam[0] = _createMonWithSpeed(Type.Fire, 100, 50, 10, 100); cpuTeam[0].moves = moves; - cpuTeam[1] = _createMon(Type.Fire, 100, 50, 10); - cpuTeam[1].stats.speed = 100; + cpuTeam[1] = _createMonWithSpeed(Type.Fire, 100, 50, 10, 100); cpuTeam[1].moves = moves; Mon[] memory aliceTeam = new Mon[](2); diff --git a/test/EngineGasTest.sol b/test/EngineGasTest.sol index a60e285c..1dae1d6e 100644 --- a/test/EngineGasTest.sol +++ b/test/EngineGasTest.sol @@ -49,16 +49,6 @@ contract EngineGasTest is Test, BattleHelper { TestTeamRegistry defaultRegistry; DefaultMatchmaker matchmaker; - // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { - return uint16( - (playerIndex & 0x1) - | ((monIndex & 0x7) << 1) - | ((statIndex & 0xF) << 4) - | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) - ); - } - function setUp() public { defaultOracle = new DefaultRandomnessOracle(); engine = new Engine(0, 0, 0); diff --git a/test/InlineEngineGasTest.sol b/test/InlineEngineGasTest.sol index 1c447f45..19842928 100644 --- a/test/InlineEngineGasTest.sol +++ b/test/InlineEngineGasTest.sol @@ -15,7 +15,7 @@ import {SignedCommitManager} from "../src/commit-manager/SignedCommitManager.sol import {Engine} from "../src/Engine.sol"; import {IEngine} from "../src/IEngine.sol"; import {IValidator} from "../src/IValidator.sol"; -import {EIP712} from "../src/lib/EIP712.sol"; +import {SignedCommitHelper} from "./abstract/SignedCommitHelper.sol"; import {IEffect} from "../src/effects/IEffect.sol"; import {StaminaRegen} from "../src/effects/StaminaRegen.sol"; @@ -60,16 +60,6 @@ contract InlineEngineGasTest is Test, BattleHelper { uint256 constant MONS_PER_TEAM = 4; uint256 constant MOVES_PER_MON = 4; - // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { - return uint16( - (playerIndex & 0x1) - | ((monIndex & 0x7) << 1) - | ((statIndex & 0xF) << 4) - | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) - ); - } - function setUp() public { defaultOracle = new DefaultRandomnessOracle(); // Create engine with inline validation defaults @@ -569,7 +559,7 @@ contract InlineEngineGasTest is Test, BattleHelper { /// SignedMatchmaker (no propose/accept/confirm storage), and /// SignedCommitManager::executeWithDualSignedMoves (1 TX per two-player turn). /// @dev Forced single-player switches after KOs use SignedCommitManager::executeSinglePlayerMove. -contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { +contract FullyOptimizedInlineGasTest is BattleHelper, SignedCommitHelper { uint256 constant MONS_PER_TEAM = 4; uint256 constant MOVES_PER_MON = 4; @@ -590,20 +580,6 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { mapping(bytes32 => bool) private _seenSlot; bytes32[] private _seenKeys; - function _domainNameAndVersion() internal pure override returns (string memory, string memory) { - return ("SignedCommitManager", "1"); - } - - // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { - return uint16( - (playerIndex & 0x1) - | ((monIndex & 0x7) << 1) - | ((statIndex & 0xF) << 4) - | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) - ); - } - function setUp() public { p0 = vm.addr(P0_PK); p1 = vm.addr(P1_PK); @@ -657,61 +633,6 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { return battleKey; } - function _signDualReveal( - uint256 privateKey, - bytes32 battleKey, - uint64 turnId, - bytes32 committerMoveHash, - uint8 revealerMoveIndex, - uint104 revealerSalt, - uint16 revealerExtraData - ) internal view returns (bytes memory) { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashDualSignedReveal( - SignedCommitLib.DualSignedReveal({ - battleKey: battleKey, - turnId: turnId, - committerMoveHash: committerMoveHash, - revealerMoveIndex: revealerMoveIndex, - revealerSalt: revealerSalt, - revealerExtraData: revealerExtraData - }) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - - function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) - internal - view - returns (bytes memory) - { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashSignedCommit( - SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - /// @dev Executes a two-player turn in 1 TX via executeWithDualSignedMoves. /// p0Move/p1Move semantics match _commitRevealExecuteForAliceAndBob so the /// battle scripts can be transcribed directly from the non-optimized test. @@ -754,9 +675,10 @@ contract FullyOptimizedInlineGasTest is Test, BattleHelper, EIP712 { bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); - bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); + address mgr = address(signedCommitManager); + bytes memory committerSig = _signCommit(mgr, committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( - revealerPk, battleKey, turnId, committerMoveHash, + mgr, revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); diff --git a/test/SignedCommitManager.t.sol b/test/SignedCommitManager.t.sol index dbdc39a4..43abefe3 100644 --- a/test/SignedCommitManager.t.sol +++ b/test/SignedCommitManager.t.sol @@ -17,11 +17,11 @@ import {MockRandomnessOracle} from "./mocks/MockRandomnessOracle.sol"; import {TestTeamRegistry} from "./mocks/TestTeamRegistry.sol"; import {DefaultMatchmaker} from "../src/matchmaker/DefaultMatchmaker.sol"; import {BattleHelper} from "./abstract/BattleHelper.sol"; +import {SignedCommitHelper} from "./abstract/SignedCommitHelper.sol"; import {SignedCommitLib} from "../src/commit-manager/SignedCommitLib.sol"; import {TestMoveFactory} from "./mocks/TestMoveFactory.sol"; -import {EIP712} from "../src/lib/EIP712.sol"; -abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { +abstract contract SignedCommitManagerTestBase is BattleHelper, SignedCommitHelper { Engine engine; SignedCommitManager signedCommitManager; MockRandomnessOracle mockOracle; @@ -36,11 +36,6 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { address p0; address p1; - // Required by EIP712 inheritance (only used to access _DOMAIN_TYPEHASH) - function _domainNameAndVersion() internal pure override returns (string memory, string memory) { - return ("SignedCommitManager", "1"); - } - function setUp() public virtual { p0 = vm.addr(P0_PK); p1 = vm.addr(P1_PK); @@ -130,83 +125,6 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { return battleKey; } - /// @dev Signs a DualSignedReveal struct for the revealer - /// @param privateKey The revealer's private key - /// @param battleKey The battle identifier - /// @param turnId The current turn ID - /// @param committerMoveHash The committer's move hash (that revealer signs over) - /// @param revealerMoveIndex The revealer's move index - /// @param revealerSalt The revealer's salt - /// @param revealerExtraData The revealer's extra data - function _signDualReveal( - uint256 privateKey, - bytes32 battleKey, - uint64 turnId, - bytes32 committerMoveHash, - uint8 revealerMoveIndex, - uint104 revealerSalt, - uint16 revealerExtraData - ) internal view returns (bytes memory) { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - - bytes32 structHash = SignedCommitLib.hashDualSignedReveal( - SignedCommitLib.DualSignedReveal({ - battleKey: battleKey, - turnId: turnId, - committerMoveHash: committerMoveHash, - revealerMoveIndex: revealerMoveIndex, - revealerSalt: revealerSalt, - revealerExtraData: revealerExtraData - }) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - - /// @dev Signs a SignedCommit struct for the committer - /// @param privateKey The committer's private key - /// @param moveHash The committer's move hash - /// @param battleKey The battle identifier - /// @param turnId The current turn ID - function _signCommit( - uint256 privateKey, - bytes32 moveHash, - bytes32 battleKey, - uint64 turnId - ) internal view returns (bytes memory) { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - - bytes32 structHash = SignedCommitLib.hashSignedCommit( - SignedCommitLib.SignedCommit({ - moveHash: moveHash, - battleKey: battleKey, - turnId: turnId - }) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - /// @dev Completes a turn using the normal commit-reveal flow. /// Turn 0 uses SWITCH_MOVE_INDEX; subsequent turns use NO_OP_MOVE_INDEX. function _completeTurnNormal(bytes32 battleKey, uint256 turnId) internal { @@ -245,8 +163,8 @@ abstract contract SignedCommitManagerTestBase is Test, BattleHelper, EIP712 { (uint256 committerPk, uint256 revealerPk) = turnId % 2 == 0 ? (P0_PK, P1_PK) : (P1_PK, P0_PK); bytes memory committerSignature = - _signCommit(committerPk, committerMoveHash, battleKey, uint64(turnId)); - bytes memory revealerSignature = _signDualReveal( + _signCommit(address(signedCommitManager), committerPk, committerMoveHash, battleKey, uint64(turnId)); + bytes memory revealerSignature = _signDualReveal(address(signedCommitManager), revealerPk, battleKey, uint64(turnId), committerMoveHash, moveIndex, revealerSalt, 0 ); @@ -285,9 +203,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // p0 signs their commitment, p1 signs their move + p0's hash - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, turnId); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, turnId); uint104 p1Salt = uint104(2); - bytes memory p1Signature = _signDualReveal( + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, turnId, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -324,10 +242,10 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p1Salt = uint104(2); bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); - bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, turnId); + bytes memory p1CommitSig = _signCommit(address(signedCommitManager), P1_PK, p1MoveHash, battleKey, turnId); uint104 p0Salt = uint104(3); - bytes memory p0Signature = _signDualReveal( + bytes memory p0Signature = _signDualReveal(address(signedCommitManager), P0_PK, battleKey, turnId, p1MoveHash, NO_OP_MOVE_INDEX, p0Salt, 0 ); @@ -413,7 +331,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // Valid committer sig, but garbage revealer sig. - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); bytes memory invalidSignature = abi.encodePacked(bytes32(uint256(1)), bytes32(uint256(2)), uint8(27)); vm.startPrank(p0); @@ -437,9 +355,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p0Salt = uint104(1); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); // p0 signs the revealer slot instead of p1 (wrong signer - should be revealer p1) - bytes memory wrongSignature = _signDualReveal( + bytes memory wrongSignature = _signDualReveal(address(signedCommitManager), P0_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -470,8 +388,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); // Both signatures bound to turnId=0, replayed at turnId=2 - bytes memory turn0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); - bytes memory turn0Signature = _signDualReveal( + bytes memory turn0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); + bytes memory turn0Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, NO_OP_MOVE_INDEX, uint104(0), 0 ); @@ -497,8 +415,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // Both signatures bound to battle 1 - bytes memory battle1CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey1, 0); - bytes memory battle1Signature = _signDualReveal( + bytes memory battle1CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey1, 0); + bytes memory battle1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey1, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -536,12 +454,12 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { ); // p1 signs the DualSignedReveal binding themselves to a chosen committer preimage - bytes memory p1Signature = _signDualReveal( + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, chosenCommitterMoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); // Attacker forges a "committer signature" (signed by themselves, P1, over the same hash). - bytes memory forgedCommitterSig = _signCommit(P1_PK, chosenCommitterMoveHash, battleKey, 0); + bytes memory forgedCommitterSig = _signCommit(address(signedCommitManager), P1_PK, chosenCommitterMoveHash, battleKey, 0); // _startBattleWith leaves an active prank on p0; clear it. vm.stopPrank(); @@ -570,8 +488,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p1Salt = uint104(2); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -604,8 +522,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // p1 signs the SignedCommit instead of p0 → recovers to p1, not the committer p0. - bytes memory wrongCommitSig = _signCommit(P1_PK, p0MoveHash, battleKey, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory wrongCommitSig = _signCommit(address(signedCommitManager), P1_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -633,9 +551,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0DifferentMoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); // committer signs over a different move - bytes memory mismatchedCommitSig = _signCommit(P0_PK, p0DifferentMoveHash, battleKey, 0); + bytes memory mismatchedCommitSig = _signCommit(address(signedCommitManager), P0_PK, p0DifferentMoveHash, battleKey, 0); // Revealer signs the same different hash so the revealer side would have validated - bytes memory p1Signature = _signDualReveal( + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0DifferentMoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -667,8 +585,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p0Salt = uint104(1); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, fakeBattleKey, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, fakeBattleKey, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, fakeBattleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -700,8 +618,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); // Both signatures are bound to turnId=0 (replay attempt) - bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, 0); - bytes memory p0Signature = _signDualReveal( + bytes memory p1CommitSig = _signCommit(address(signedCommitManager), P1_PK, p1MoveHash, battleKey, 0); + bytes memory p0Signature = _signDualReveal(address(signedCommitManager), P0_PK, battleKey, 0, p1MoveHash, NO_OP_MOVE_INDEX, uint104(0), 0 ); @@ -726,8 +644,8 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p0Salt = uint104(1); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -768,11 +686,11 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0RealMoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // p0 signs the commitment for the REAL move hash (matches what they'll submit) - bytes memory p0CommitSig = _signCommit(P0_PK, p0RealMoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0RealMoveHash, battleKey, 0); // p1 signs over a DIFFERENT hash than what p0 will submit bytes32 fakeP0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(999), uint16(0))); - bytes memory p1Signature = _signDualReveal( + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, fakeP0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -799,9 +717,9 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p0Salt = uint104(1); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); // p1 signs with SWITCH_MOVE_INDEX - bytes memory p1Signature = _signDualReveal( + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, uint104(0), 0 ); @@ -833,7 +751,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // p0 signs their commitment - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); // p1 (revealer) publishes p0's commitment on-chain vm.startPrank(p1); @@ -866,7 +784,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p1MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p1Salt, uint16(0))); // p1 signs their commitment - bytes memory p1CommitSig = _signCommit(P1_PK, p1MoveHash, battleKey, 1); + bytes memory p1CommitSig = _signCommit(address(signedCommitManager), P1_PK, p1MoveHash, battleKey, 1); // p0 (revealer) publishes p1's commitment on-chain vm.startPrank(p0); @@ -892,7 +810,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { uint104 p0Salt = uint104(1); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); // Even p0 themselves can submit their own signed commitment // (though this is equivalent to just calling commitMove) @@ -910,7 +828,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p1 signs instead of p0 (wrong signer) - bytes memory wrongSig = _signCommit(P1_PK, p0MoveHash, battleKey, 0); + bytes memory wrongSig = _signCommit(address(signedCommitManager), P1_PK, p0MoveHash, battleKey, 0); vm.startPrank(p1); vm.expectRevert(SignedCommitManager.InvalidSignature.selector); @@ -923,7 +841,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p0 signs for turn 1 instead of turn 0 - bytes memory wrongTurnSig = _signCommit(P0_PK, p0MoveHash, battleKey, 1); + bytes memory wrongTurnSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 1); vm.startPrank(p1); vm.expectRevert(SignedCommitManager.InvalidSignature.selector); @@ -937,7 +855,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); // p0 signs for battle 1 - bytes memory battle1Sig = _signCommit(P0_PK, p0MoveHash, battleKey1, 0); + bytes memory battle1Sig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey1, 0); // Try to use on battle 2 vm.startPrank(p1); @@ -949,7 +867,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { bytes32 battleKey = _startBattleWith(address(signedCommitManager)); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); // First commit succeeds vm.startPrank(p1); @@ -963,7 +881,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { function test_commitWithSignature_revert_battleNotStarted() public { bytes32 fakeBattleKey = bytes32(uint256(123)); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, uint104(1), uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, fakeBattleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, fakeBattleKey, 0); vm.startPrank(p1); vm.expectRevert(DefaultCommitManager.BattleNotYetStarted.selector); @@ -980,7 +898,7 @@ contract SignedCommitManagerTest is SignedCommitManagerTestBase { signedCommitManager.commitMove(battleKey, p0MoveHash); // Now trying to commit with signature should fail - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); vm.startPrank(p1); vm.expectRevert(DefaultCommitManager.AlreadyCommited.selector); signedCommitManager.commitWithSignature(battleKey, p0MoveHash, p0CommitSig); @@ -1015,8 +933,8 @@ contract SignedCommitManagerEngineSafetyTest is SignedCommitManagerTestBase { (uint256 committerPk, uint256 revealerPk, address committerAddr) = turnId % 2 == 0 ? (P0_PK, P1_PK, p0) : (P1_PK, P0_PK, p1); - bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); - bytes memory revealerSig = _signDualReveal( + bytes memory committerSig = _signCommit(address(signedCommitManager), committerPk, committerMoveHash, battleKey, turnId); + bytes memory revealerSig = _signDualReveal(address(signedCommitManager), revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); diff --git a/test/SignedCommitManagerGasBenchmark.t.sol b/test/SignedCommitManagerGasBenchmark.t.sol index 25cc12ac..06a732c3 100644 --- a/test/SignedCommitManagerGasBenchmark.t.sol +++ b/test/SignedCommitManagerGasBenchmark.t.sol @@ -60,8 +60,8 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); // Both players sign off-chain - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -128,8 +128,8 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { uint104 p1Salt = uint104(101); bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey, 2); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey, 2); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey, 2, p0MoveHash, NO_OP_MOVE_INDEX, p1Salt, 0 ); @@ -186,8 +186,8 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { uint104 p1Salt = uint104(2); bytes32 p0MoveHash = keccak256(abi.encodePacked(SWITCH_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey2, 0); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey2, 0); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey2, 0, p0MoveHash, SWITCH_MOVE_INDEX, p1Salt, 0 ); @@ -240,8 +240,8 @@ contract SignedCommitManagerGasBenchmarkTest is SignedCommitManagerTestBase { uint104 p1Salt = uint104(101); bytes32 p0MoveHash = keccak256(abi.encodePacked(NO_OP_MOVE_INDEX, p0Salt, uint16(0))); - bytes memory p0CommitSig = _signCommit(P0_PK, p0MoveHash, battleKey2, 2); - bytes memory p1Signature = _signDualReveal( + bytes memory p0CommitSig = _signCommit(address(signedCommitManager), P0_PK, p0MoveHash, battleKey2, 2); + bytes memory p1Signature = _signDualReveal(address(signedCommitManager), P1_PK, battleKey2, 2, p0MoveHash, NO_OP_MOVE_INDEX, p1Salt, 0 ); diff --git a/test/StandardAttackPvPGasTest.sol b/test/StandardAttackPvPGasTest.sol index 9f981f30..a0d2e1b0 100644 --- a/test/StandardAttackPvPGasTest.sol +++ b/test/StandardAttackPvPGasTest.sol @@ -14,7 +14,7 @@ import {SignedMatchmaker} from "../src/matchmaker/SignedMatchmaker.sol"; import {BattleOfferLib} from "../src/matchmaker/BattleOfferLib.sol"; import {StandardAttackFactory} from "../src/moves/StandardAttackFactory.sol"; import {ATTACK_PARAMS} from "../src/moves/StandardAttackStructs.sol"; -import {EIP712} from "../src/lib/EIP712.sol"; +import {SignedCommitHelper} from "./abstract/SignedCommitHelper.sol"; import {IEngine} from "../src/IEngine.sol"; import {IEngineHook} from "../src/IEngineHook.sol"; @@ -38,7 +38,7 @@ import {TestTeamRegistry} from "./mocks/TestTeamRegistry.sol"; /// Existing PvP benchmarks (FullyOptimizedInlineGasTest) use CustomAttack / /// EffectAttack / StatBoostsMove — none of which extend StandardAttack — so the /// StandardAttack hot path doesn't show up there. -contract StandardAttackPvPGasTest is Test, EIP712 { +contract StandardAttackPvPGasTest is SignedCommitHelper { uint256 constant MONS_PER_TEAM = 4; uint256 constant MOVES_PER_MON = 4; @@ -55,10 +55,6 @@ contract StandardAttackPvPGasTest is Test, EIP712 { TestTeamRegistry defaultRegistry; StandardAttackFactory attackFactory; - function _domainNameAndVersion() internal pure override returns (string memory, string memory) { - return ("SignedCommitManager", "1"); - } - function setUp() public { p0 = vm.addr(P0_PK); p1 = vm.addr(P1_PK); @@ -116,61 +112,6 @@ contract StandardAttackPvPGasTest is Test, EIP712 { return battleKey; } - function _signDualReveal( - uint256 privateKey, - bytes32 battleKey, - uint64 turnId, - bytes32 committerMoveHash, - uint8 revealerMoveIndex, - uint104 revealerSalt, - uint16 revealerExtraData - ) internal view returns (bytes memory) { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashDualSignedReveal( - SignedCommitLib.DualSignedReveal({ - battleKey: battleKey, - turnId: turnId, - committerMoveHash: committerMoveHash, - revealerMoveIndex: revealerMoveIndex, - revealerSalt: revealerSalt, - revealerExtraData: revealerExtraData - }) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - - function _signCommit(uint256 privateKey, bytes32 moveHash, bytes32 battleKey, uint64 turnId) - internal - view - returns (bytes memory) - { - bytes32 domainSeparator = keccak256( - abi.encode( - _DOMAIN_TYPEHASH, - keccak256("SignedCommitManager"), - keccak256("1"), - block.chainid, - address(signedCommitManager) - ) - ); - bytes32 structHash = SignedCommitLib.hashSignedCommit( - SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) - ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - return abi.encodePacked(r, s, v); - } - function _fastTurn( bytes32 battleKey, uint8 p0MoveIndex, @@ -210,9 +151,10 @@ contract StandardAttackPvPGasTest is Test, EIP712 { bytes32 committerMoveHash = keccak256(abi.encodePacked(committerMoveIndex, committerSalt, committerExtraData)); - bytes memory committerSig = _signCommit(committerPk, committerMoveHash, battleKey, turnId); + address mgr = address(signedCommitManager); + bytes memory committerSig = _signCommit(mgr, committerPk, committerMoveHash, battleKey, turnId); bytes memory revealerSig = _signDualReveal( - revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData + mgr, revealerPk, battleKey, turnId, committerMoveHash, revealerMoveIndex, revealerSalt, revealerExtraData ); vm.prank(committer); diff --git a/test/abstract/BattleHelper.sol b/test/abstract/BattleHelper.sol index bca3115f..c32d6a3d 100644 --- a/test/abstract/BattleHelper.sol +++ b/test/abstract/BattleHelper.sol @@ -170,4 +170,19 @@ abstract contract BattleHelper is Test { ability: 0 }); } + + /// @dev Layout used by `test/mocks/StatBoostsMove.sol`: + /// `[boostAmount:8 (signed) | statIndex:4 | monIndex:3 | playerIndex:1]` + function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) + internal + pure + returns (uint16) + { + return uint16( + (playerIndex & 0x1) + | ((monIndex & 0x7) << 1) + | ((statIndex & 0xF) << 4) + | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) + ); + } } diff --git a/test/abstract/SignedCommitHelper.sol b/test/abstract/SignedCommitHelper.sol new file mode 100644 index 00000000..5193f7c5 --- /dev/null +++ b/test/abstract/SignedCommitHelper.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import {SignedCommitLib} from "../../src/commit-manager/SignedCommitLib.sol"; + +import {Test} from "forge-std/Test.sol"; + +/// @notice EIP-712 signing helpers for `SignedCommitManager` tests. +/// @dev Standalone (not inheriting `EIP712`) — replicates `_DOMAIN_TYPEHASH` to avoid pulling +/// the production base into test contracts. The verifying-contract address is taken as a +/// parameter so a single helper instance can sign for multiple managers. +abstract contract SignedCommitHelper is Test { + /// `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")` + bytes32 internal constant _SIGNED_COMMIT_DOMAIN_TYPEHASH = + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; + + function _signedCommitDomainSeparator(address verifyingContract) internal view returns (bytes32) { + return keccak256( + abi.encode( + _SIGNED_COMMIT_DOMAIN_TYPEHASH, + keccak256("SignedCommitManager"), + keccak256("1"), + block.chainid, + verifyingContract + ) + ); + } + + function _signCommit( + address signedCommitManagerAddr, + uint256 privateKey, + bytes32 moveHash, + bytes32 battleKey, + uint64 turnId + ) internal view returns (bytes memory) { + bytes32 structHash = SignedCommitLib.hashSignedCommit( + SignedCommitLib.SignedCommit({moveHash: moveHash, battleKey: battleKey, turnId: turnId}) + ); + bytes32 digest = keccak256( + abi.encodePacked("\x19\x01", _signedCommitDomainSeparator(signedCommitManagerAddr), structHash) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function _signDualReveal( + address signedCommitManagerAddr, + uint256 privateKey, + bytes32 battleKey, + uint64 turnId, + bytes32 committerMoveHash, + uint8 revealerMoveIndex, + uint104 revealerSalt, + uint16 revealerExtraData + ) internal view returns (bytes memory) { + bytes32 structHash = SignedCommitLib.hashDualSignedReveal( + SignedCommitLib.DualSignedReveal({ + battleKey: battleKey, + turnId: turnId, + committerMoveHash: committerMoveHash, + revealerMoveIndex: revealerMoveIndex, + revealerSalt: revealerSalt, + revealerExtraData: revealerExtraData + }) + ); + bytes32 digest = keccak256( + abi.encodePacked("\x19\x01", _signedCommitDomainSeparator(signedCommitManagerAddr), structHash) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } +} diff --git a/test/effects/StatBoosts.t.sol b/test/effects/StatBoosts.t.sol index 8ddd614e..bd0de342 100644 --- a/test/effects/StatBoosts.t.sol +++ b/test/effects/StatBoosts.t.sol @@ -38,16 +38,6 @@ contract StatBoostsTest is Test, BattleHelper { StatBoostsMove statBoostMove; DefaultMatchmaker matchmaker; - // Pack into 16 bits: [boostAmount:8 | statIndex:4 | monIndex:3 | playerIndex:1] - function _packStatBoost(uint256 playerIndex, uint256 monIndex, uint256 statIndex, int32 boostAmount) internal pure returns (uint16) { - return uint16( - (playerIndex & 0x1) - | ((monIndex & 0x7) << 1) - | ((statIndex & 0xF) << 4) - | ((uint256(uint8(int8(boostAmount))) & 0xFF) << 8) - ); - } - function setUp() public { typeCalc = new TestTypeCalculator(); mockOracle = new MockRandomnessOracle();