Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Feb 5, 2024
1 parent ff4e9cd commit c9a8334
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 41 deletions.
87 changes: 69 additions & 18 deletions contracts/modules/CoreActions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,39 +100,25 @@ contract CoreActions is ICoreActions, EIP712 {
external
returns (address[] memory targetAliases, address[][] memory actorAliases)
{
_validateArrayLengths(r);

uint256 n = r.targets.length;
address[] memory resolvedTargets;
address[][] memory resolvedActors = new address[][](n);
actorAliases = new address[][](n);

// Check input array lengths and resolve aliases.
// Resolve and register aliases.
unchecked {
if (n != r.actors.length) revert ArrayLengthsMismatch();
if (n != r.timestamps.length) revert ArrayLengthsMismatch();

IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry);
(resolvedTargets, targetAliases) = registry.resolveAndRegister(r.targets);

for (uint256 i; i != n; ++i) {
if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch();
(resolvedActors[i], actorAliases[i]) = registry.resolveAndRegister(r.actors[i]);
}
}

// Check the signature and invalidate the nonce.
{
bytes32 digest = _hashTypedData(
keccak256(
abi.encode(
CORE_ACTION_REGISTRATIONS_TYPEHASH,
r.coreActionType, // uint256
_hashOf(resolvedTargets), // address[]
_hashOf(resolvedActors), // address[][]
_hashOf(r.timestamps), // uint256[][]
r.nonce // uint256
)
)
);
bytes32 digest = _computeDigest(r, resolvedTargets, resolvedActors);

address signer = platformSigner[r.platform];
if (!SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, r.signature))
Expand Down Expand Up @@ -283,10 +269,60 @@ contract CoreActions is ICoreActions, EIP712 {
}
}

/**
* @inheritdoc ICoreActions
*/
function computeDigest(CoreActionRegistrations calldata r) external view returns (bytes32) {
_validateArrayLengths(r);

uint256 n = r.targets.length;
address[] memory resolvedTargets;
address[][] memory resolvedActors = new address[][](n);

// Resolve aliases.
unchecked {
IAddressAliasRegistry registry = IAddressAliasRegistry(addressAliasRegistry);
(resolvedTargets, ) = registry.resolve(r.targets);
for (uint256 i; i != n; ++i) {
if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch();
(resolvedActors[i], ) = registry.resolve(r.actors[i]);
}
}

return _computeDigest(r, resolvedTargets, resolvedActors);
}

// =============================================================
// INTERNAL / PRIVATE HELPERS
// =============================================================

/**
* @dev Returns the digest for `r`, with `resolvedTargets` and `resolvedActors`.
* @param r The core actions to register.
* @param resolvedTargets The list of resolved targets.
* @param resolvedActors The list of resolved actors.
* @return The computed digest.
*/
function _computeDigest(
CoreActionRegistrations calldata r,
address[] memory resolvedTargets,
address[][] memory resolvedActors
) internal view returns (bytes32) {
return
_hashTypedData(
keccak256(
abi.encode(
CORE_ACTION_REGISTRATIONS_TYPEHASH,
r.coreActionType, // uint256
_hashOf(resolvedTargets), // address[]
_hashOf(resolvedActors), // address[][]
_hashOf(r.timestamps), // uint256[][]
r.nonce // uint256
)
)
);
}

/**
* @dev Override for EIP-712.
* @return name_ The EIP-712 name.
Expand All @@ -303,6 +339,21 @@ contract CoreActions is ICoreActions, EIP712 {
version_ = "1";
}

/**
* @dev Validate the array lengths.
* @param r The core actions to register.
*/
function _validateArrayLengths(CoreActionRegistrations calldata r) internal pure {
unchecked {
uint256 n = r.targets.length;
if (n != r.actors.length) revert ArrayLengthsMismatch();
if (n != r.timestamps.length) revert ArrayLengthsMismatch();
for (uint256 i; i != n; ++i) {
if (r.actors[i].length != r.timestamps[i].length) revert ArrayLengthsMismatch();
}
}
}

/**
* @dev Returns the hash of `a`.
* @param a The input to hash.
Expand Down
7 changes: 7 additions & 0 deletions contracts/modules/interfaces/ICoreActions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ interface ICoreActions {
*/
function CORE_ACTION_REGISTRATIONS_TYPEHASH() external pure returns (bytes32);

/**
* @dev Returns the digest for the core actions to register.
* @param r The core actions to register.
* @return The computed value.
*/
function computeDigest(CoreActionRegistrations memory r) external view returns (bytes32);

/**
* @dev Returns the configured signer for `platform`.
* @param platform The platform.
Expand Down
151 changes: 128 additions & 23 deletions tests/modules/CoreActions.t.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,138 @@
pragma solidity ^0.8.16;

// import { IERC721AUpgradeable, ISoundEditionV2_1, SoundEditionV2_1 } from "@core/SoundEditionV2_1.sol";
// import { ISuperMinterV2, SuperMinterV2 } from "@modules/SuperMinterV2.sol";
// import { IPlatformAirdropper, PlatformAirdropper } from "@modules/PlatformAirdropper.sol";
// import { IAddressAliasRegistry, AddressAliasRegistry } from "@modules/AddressAliasRegistry.sol";
// import { LibOps } from "@core/utils/LibOps.sol";
// import { Ownable } from "solady/auth/Ownable.sol";
// import { LibZip } from "solady/utils/LibZip.sol";
// import { SafeCastLib } from "solady/utils/SafeCastLib.sol";
// import { LibSort } from "solady/utils/LibSort.sol";
import { ICoreActions, CoreActions } from "@modules/CoreActions.sol";
import { IAddressAliasRegistry, AddressAliasRegistry } from "@modules/AddressAliasRegistry.sol";
import { EnumerableMap } from "openzeppelin/utils/structs/EnumerableMap.sol";
import { LibSort } from "solady/utils/LibSort.sol";
import "../TestConfigV2_1.sol";

contract CoreActionsTests is TestConfigV2_1 {
using EnumerableMap for *;

AddressAliasRegistry aar;
CoreActions ca;

EnumerableMap.Bytes32ToUintMap expectedTimestamps;

function setUp() public virtual override {
super.setUp();
// ISoundEditionV2_1.EditionInitialization memory init = genericEditionInitialization();
// init.tierCreations = new ISoundEditionV2_1.TierCreation[](2);
// init.tierCreations[0].tier = 0;
// init.tierCreations[1].tier = 1;
// init.tierCreations[1].maxMintableLower = type(uint32).max;
// init.tierCreations[1].maxMintableUpper = type(uint32).max;
// edition = createSoundEdition(init);
// sm = new SuperMinterV2();
// edition.grantRoles(address(sm), edition.MINTER_ROLE());
// aar = new AddressAliasRegistry();
// pa = new PlatformAirdropper(address(aar));
}

// function _computeDigest()
aar = new AddressAliasRegistry();
ca = new CoreActions(address(aar));
}

struct _TestTemps {
address platform;
uint256 platformSignerPrivateKey;
address platformSigner;
address[] targetAliases;
address[][] actorAliases;
address[] targets;
address[] actors;
uint32[] timestamps;
}

function testRegisterCoreActions(uint256) public {
_TestTemps memory t;
t.platform = _randomNonZeroAddress();
(t.platformSigner, t.platformSignerPrivateKey) = _randomSigner();

vm.prank(t.platform);
ca.setPlatformSigner(t.platformSigner);

CoreActions.CoreActionRegistrations memory rs;
rs.platform = t.platform;
rs.coreActionType = _random();
rs.targets = _randomNonZeroAddressesGreaterThan();
rs.actors = new address[][](rs.targets.length);
rs.timestamps = new uint32[][](rs.targets.length);
rs.nonce = _random();
for (uint256 i; i != rs.targets.length; ++i) {
rs.actors[i] = _randomNonZeroAddressesGreaterThan();
rs.timestamps[i] = _randomTimestamps(rs.actors[i].length);
for (uint256 j; j != rs.actors[i].length; ++j) {
bytes32 h = keccak256(abi.encodePacked(rs.targets[i], rs.actors[i][j]));
if (!expectedTimestamps.contains(h)) {
expectedTimestamps.set(h, rs.timestamps[i][j]);
}
}
}
rs.signature = _generateSignature(rs, t.platformSignerPrivateKey);

(t.targetAliases, t.actorAliases) = ca.register(rs);

for (uint256 i; i != rs.targets.length; ++i) {
for (uint256 j; j != rs.actors[i].length; ++j) {
uint32 timestamp = ca.getCoreActionTimestamp(
rs.platform,
rs.coreActionType,
rs.targets[i],
rs.actors[i][j]
);
bytes32 h = keccak256(abi.encodePacked(rs.targets[i], rs.actors[i][j]));
assertEq(timestamp, expectedTimestamps.get(h));
}
}

uint256 actionsSum;
t.targets = LibSort.difference(rs.targets, new address[](0));
LibSort.sort(t.targets);
LibSort.uniquifySorted(t.targets);
for (uint256 i; i != t.targets.length; ++i) {
(t.actors, t.timestamps) = ca.getCoreActions(rs.platform, rs.coreActionType, t.targets[i]);
assertEq(t.actors.length, t.timestamps.length);
actionsSum += t.actors.length;
for (uint256 j; j != t.actors.length; ++j) {
bytes32 h = keccak256(abi.encodePacked(t.targets[i], t.actors[j]));
assertEq(t.timestamps[j], expectedTimestamps.get(h));
}
}
assertEq(actionsSum, expectedTimestamps.length());

vm.expectRevert(ICoreActions.InvalidSignature.selector);
ca.register(rs);
}

function _generateSignature(CoreActions.CoreActionRegistrations memory rs, uint256 privateKey)
internal
returns (bytes memory signature)
{
bytes32 digest = ca.computeDigest(rs);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
signature = abi.encodePacked(r, s, v);
}

function _randomNonZeroAddressesGreaterThan() internal returns (address[] memory a) {
a = _randomNonZeroAddressesGreaterThan(0xffffffff);
}

function _randomNonZeroAddressesGreaterThan(uint256 t) internal returns (address[] memory a) {
uint256 n = _random() % 4;
if (_random() % 32 == 0) {
n = _random() % 32;
}
a = new address[](n);
require(t != 0, "t must not be zero");
unchecked {
for (uint256 i; i != n; ++i) {
uint256 r;
if (_random() & 1 == 0) {
while (r <= t) r = uint256(uint160(_random()));
} else {
r = type(uint256).max ^ _bound(_random(), 1, 8);
}
a[i] = address(uint160(r));
}
}
}

function _randomTimestamps(uint256 n) internal returns (uint32[] memory a) {
a = new uint32[](n);
unchecked {
for (uint256 i; i != n; ++i) {
a[i] = uint32(_bound(_random(), 1, type(uint32).max));
}
}
}

function _hashOf(address[] memory a) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(a));
Expand Down

0 comments on commit c9a8334

Please sign in to comment.