-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Base test, remove
renderer
, and add PurityChecker
to `CurtaGo…
…lf` constructors (#7) * tests: add BaseTest util test contract * feat: remove renderer and add puritychecker to constructor * fix: emitted event args * feat: add mock course + solutions * tests: add mock course to base test * fix: add to * chore: add sample solution bytecodes
- Loading branch information
1 parent
425c8aa
commit 10006d3
Showing
9 changed files
with
290 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,15 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.21; | ||
|
||
import { IPurityChecker } from "../interfaces/IPurityChecker.sol"; | ||
|
||
/// @title EVM bytecode purity checker | ||
/// @notice A purity checker checks whether a given contract is pure, i.e. that | ||
/// it's restricted to a set of instructions/opcodes, by analyzing its bytecode. | ||
contract PurityChecker is IPurityChecker { | ||
/// @inheritdoc IPurityChecker | ||
function check(bytes memory _code) external view override returns (bool) { | ||
// TODO | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: MIT | ||
import { ICourse } from "../../interfaces/ICourse.sol"; | ||
|
||
/// @title A mock Curta Golf Course for testing | ||
/// @author fiveoutofnine | ||
contract MockCourse is ICourse { | ||
/// @inheritdoc ICourse | ||
function name() external pure returns (string memory) { | ||
return "Mock Course"; | ||
} | ||
|
||
/// @inheritdoc ICourse | ||
function run(address _target, uint256 _seed) external view returns (uint32) { | ||
uint256 start = gasleft(); | ||
|
||
// Generate inputs from `_seed`. | ||
uint256 a = _seed >> 128; | ||
uint256 b = _seed & 0xffffffffffffffffffffffffffffffff; | ||
|
||
// Run solution. | ||
uint256 c = IMockCourse(_target).add(a, b); | ||
uint256 end = gasleft(); | ||
|
||
unchecked { | ||
// Verify solution. | ||
if (c != a + b) revert IncorrectSolution(); | ||
|
||
// Return gas used. | ||
return uint32(start - end); | ||
} | ||
} | ||
} | ||
|
||
/// @title The interface for `MockCourse`, a mock Curta Golf Course for testing | ||
interface IMockCourse { | ||
/// @notice Adds two numbers together. | ||
/// @param _a The first number. | ||
/// @param _b The second number. | ||
/// @return The sum of `_a` and `_b`. | ||
function add(uint256 _a, uint256 _b) external pure returns (uint256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// SPDX-License-Identifier: MIT | ||
import { IMockCourse } from "./MockCourse.sol"; | ||
|
||
/// @title An efficient solution to `MockCourse`. | ||
/// @author fiveoutofnine | ||
/// @dev When compiled with `0.8.21+commit.d9974bed` and `1_000_000` optimizer | ||
/// runs, the contract has the following bytecode: | ||
/// ``` | ||
/// 0x6080604052348015600f57600080fd5b5060a58061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b603c6038366004604e565b0190565b60405190815260200160405180910390f35b60008060408385031215606057600080fd5b5050803592602090910135915056fea264697066735822122053508e1f6f437dc11678aec86f624d167eb4ae75e36f622b6bf518e6edd2a99f64736f6c63430008150033 | ||
/// ``` | ||
contract MockCourseSolutionEfficient is IMockCourse { | ||
/// @inheritdoc IMockCourse | ||
function add(uint256 _a, uint256 _b) external pure override returns (uint256) { | ||
unchecked { | ||
return _a + _b; | ||
} | ||
} | ||
} | ||
|
||
/// @title An inefficient solution to `MockCourse`. | ||
/// @author fiveoutofnine | ||
/// @dev When compiled with `0.8.21+commit.d9974bed` and `1_000_000` optimizer | ||
/// runs, the contract has the following bytecode: | ||
/// ``` | ||
/// 0x608060405234801561001057600080fd5b50610158806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063771602f714610030575b600080fd5b61004361003e366004610086565b610055565b60405190815260200160405180910390f35b6000805b60648110156100725761006b816100d7565b9050610059565b5061007d828461010f565b90505b92915050565b6000806040838503121561009957600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610108576101086100a8565b5060010190565b80820180821115610080576100806100a856fea26469706673582212200d824e76c3f79d5d524f08c7f5df03e9eee461a0664d228713dc578a86c02fc564736f6c63430008150033 | ||
/// ``` | ||
contract MockCourseSolutionInefficient is IMockCourse { | ||
/// @inheritdoc IMockCourse | ||
function add(uint256 _a, uint256 _b) external pure override returns (uint256) { | ||
// Waste some gas. | ||
for (uint256 i; i < 100; ++i) { } | ||
|
||
return _a + _b; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.21; | ||
|
||
import { Test } from "forge-std/Test.sol"; | ||
import { LibRLP } from "solady/utils/LibRLP.sol"; | ||
|
||
import { CurtaGolf } from "../../src/CurtaGolf.sol"; | ||
import { Par } from "../../src/Par.sol"; | ||
import { ICourse } from "../../src/interfaces/ICourse.sol"; | ||
import { IPurityChecker } from "../../src/interfaces/IPurityChecker.sol"; | ||
import { PurityChecker } from "../../src/utils/PurityChecker.sol"; | ||
import { MockCourse } from "../../src/utils/mock/MockCourse.sol"; | ||
|
||
/// @notice A base test contract for Curta Golf with constants for sample | ||
/// solutions, events, labeled addresses, and helper functions for testing. When | ||
/// `BaseTest` is deployed, it sets and labels 3 addresses: `owner` (owner of | ||
/// the `CurtaGolf` deploy), `solver1`, and `solver2`. Then, in `setUp`, it | ||
/// deploys an instance of `CurtaGolf`, `MockCourse`, `Par`, `PurityChecker`, | ||
/// and adds `MockCourse` to `CurtaGolf` as `owner`. | ||
contract BaseTest is Test { | ||
// ------------------------------------------------------------------------- | ||
// Constants | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice Bytecode of an efficient solution to `MockCourse`. | ||
/// @dev The bytecode outputted when `MockCourseSolutionEfficient` is | ||
/// compiled with `0.8.21+commit.d9974bed` and `1_000_000` optimizer runs. | ||
bytes constant EFFICIENT_SOLUTION = | ||
hex"6080604052348015600f57600080fd5b5060a58061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b603c6038366004604e565b0190565b60405190815260200160405180910390f35b60008060408385031215606057600080fd5b5050803592602090910135915056fea264697066735822122053508e1f6f437dc11678aec86f624d167eb4ae75e36f622b6bf518e6edd2a99f64736f6c63430008150033"; | ||
|
||
/// @notice Bytecode of an inefficient solution to `MockCourse`. | ||
/// @dev The bytecode outputted when `MockCourseSolutionInefficient` is | ||
/// compiled with `0.8.21+commit.d9974bed` and `1_000_000` optimizer runs. | ||
bytes constant INEFFICIENT_SOLUTION = | ||
hex"608060405234801561001057600080fd5b50610158806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063771602f714610030575b600080fd5b61004361003e366004610086565b610055565b60405190815260200160405180910390f35b6000805b60648110156100725761006b816100d7565b9050610059565b5061007d828461010f565b90505b92915050565b6000806040838503121561009957600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610108576101086100a8565b5060010190565b80820180821115610080576100806100a856fea26469706673582212200d824e76c3f79d5d524f08c7f5df03e9eee461a0664d228713dc578a86c02fc564736f6c63430008150033"; | ||
|
||
// ------------------------------------------------------------------------- | ||
// `CurtaGolf` events | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice Emitted when a course is added to the contract. | ||
/// @param id The ID of the course. | ||
/// @param course The address of the course. | ||
event AddCourse(uint32 indexed id, ICourse indexed course); | ||
|
||
/// @notice Emitted when a commit for a solution is made. | ||
/// @param courseId The ID of the course. | ||
/// @param player The address of the player. | ||
/// @param key The key of the commit. | ||
event CommitSolution(uint32 indexed courseId, address indexed player, bytes32 key); | ||
|
||
/// @notice Emitted when a valid submission is made. | ||
/// @param courseId The ID of the course. | ||
/// @param recipient The address of the recipient. | ||
/// @param target The address of the deployed solution. | ||
event SubmitSolution( | ||
uint32 indexed courseId, address indexed recipient, address indexed target | ||
); | ||
|
||
/// @notice Emitted when a new purity checker is set. | ||
/// @param purityChecker The address of the new purity checker. | ||
event SetPurityChecker(IPurityChecker indexed purityChecker); | ||
|
||
/// @notice Emitted when a course gets a new King. | ||
/// @param courseId The ID of the course. | ||
/// @param recipient The address of the recipient. | ||
/// @param gasUsed The amount of gas used. | ||
event UpdateKing(uint32 indexed courseId, address indexed recipient, uint32 indexed gasUsed); | ||
|
||
// ------------------------------------------------------------------------- | ||
// `ERC721` events | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice Emitted when the address approved to transfer a token is | ||
/// updated. | ||
/// @param owner The owner of the token. | ||
/// @param spender The address approved to transfer the token. | ||
/// @param id The ID of the token. | ||
event Approval(address indexed owner, address indexed spender, uint256 indexed id); | ||
|
||
/// @notice Emitted when an operator to transfer all of an owner's tokens is | ||
/// updated. | ||
/// @param owner The owner of the tokens. | ||
/// @param operator The address approved to transfer all of the owner's | ||
/// tokens. | ||
/// @param approved The new approval status of the operator. | ||
event ApprovalForAll(address indexed owner, address indexed operator, bool approved); | ||
|
||
/// @notice Emitted when a token is transferred. | ||
/// @param from The address the token is transferred from. | ||
/// @param to The address the token is transferred to. | ||
/// @param id The ID of the token. | ||
event Transfer(address indexed from, address indexed to, uint256 indexed id); | ||
|
||
// ------------------------------------------------------------------------- | ||
// Immutable storage | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice Address of the owner | ||
address internal immutable owner; | ||
|
||
/// @notice Address to solver 1. | ||
address internal immutable solver1; | ||
|
||
/// @notice Address of a solver. | ||
address internal immutable solver2; | ||
|
||
// ------------------------------------------------------------------------- | ||
// Contracts | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice The Curta Golf contract. | ||
CurtaGolf internal curtaGolf; | ||
|
||
/// @notice A mock Curta Golf Course. | ||
ICourse internal mockCourse; | ||
|
||
/// @notice The Par contract. | ||
Par internal par; | ||
|
||
/// @notice The purity checker contract. | ||
PurityChecker internal purityChecker; | ||
|
||
// ------------------------------------------------------------------------- | ||
// Setup | ||
// ------------------------------------------------------------------------- | ||
|
||
/// @notice Sets and labels addresses for `owner`, `solver1`, `solver2`, and | ||
/// sets the . | ||
constructor() { | ||
// Set addresses. | ||
owner = makeAddr("owner"); | ||
solver1 = makeAddr("solver1"); | ||
solver2 = makeAddr("solver2"); | ||
vm.label(owner, "Curta Golf owner"); | ||
vm.label(solver1, "Solver 1"); | ||
vm.label(solver2, "Solver 2"); | ||
|
||
// Deploy solutions to `MockCourse`. | ||
/* MockCourseSolutionEfficient solutionEfficient = new MockCourseSolutionEfficient(); | ||
MockCourseSolutionInefficient solutionInefficient = new MockCourseSolutionInefficient(); | ||
efficientSolution = address(solutionEfficient).code; | ||
inefficientSolution = address(solutionInefficient).code; */ | ||
} | ||
|
||
/// @notice Deploys an instance of `CurtaGolf`, `MockCourse`, `Par`, | ||
/// `PurityChecker`, and adds `MockCourse` to `CurtaGolf` as `owner`. | ||
function setUp() public { | ||
// Transaction #1. | ||
purityChecker = new PurityChecker(); | ||
|
||
// Curta Golf will be deployed on transaction #3, and Par will be | ||
// deployed on transaction #2. | ||
address curtaGolfAddress = LibRLP.computeAddress(address(this), 3); | ||
address parAddress = LibRLP.computeAddress(address(this), 2); | ||
|
||
// Transaction #2: Deploy Par. | ||
par = new Par(curtaGolfAddress); | ||
// Transaction #3: Deploy Curta Golf. | ||
curtaGolf = new CurtaGolf(par, purityChecker); | ||
// Transaction #4: Deploy the mock course. | ||
mockCourse = new MockCourse(); | ||
|
||
// Transfer ownership of Curta Golf to `owner`. | ||
curtaGolf.transferOwnership(owner); | ||
|
||
// Add the mock course to Curta Golf. | ||
vm.prank(owner); | ||
curtaGolf.addCourse(mockCourse); | ||
|
||
// Label addresses. | ||
vm.label(address(par), "`Par`"); | ||
vm.label(address(curtaGolf), "`CurtaGolf`"); | ||
vm.label(address(mockCourse), "`MockCourse`"); | ||
} | ||
} |