Skip to content

Commit

Permalink
Merge pull request #16 from ProjectOpenSea/dan/2024/02/auth-calldata-…
Browse files Browse the repository at this point in the history
…hashing-moat

add auth calldata hashing to moat
  • Loading branch information
DJViau authored Feb 15, 2024
2 parents f97a753 + f819474 commit 192f6da
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 53 deletions.
33 changes: 31 additions & 2 deletions src/main/test/HashValidationZoneOfferer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import { ContractOffererInterface } from
"seaport-types/src/interfaces/ContractOffererInterface.sol";

import { ZoneInterface } from "seaport-types/src/interfaces/ZoneInterface.sol";

import { OffererZoneFailureReason } from "./OffererZoneFailureReason.sol";


/**
* @dev This contract is used to validate hashes. Use the
* TestTransferValidationZoneOfferer to validate transfers within the
Expand Down Expand Up @@ -59,6 +61,7 @@ contract HashValidationZoneOfferer is
error HashValidationZoneOffererValidateOrderReverts();
error HashValidationZoneOffererRatifyOrderReverts();

event AuthorizeOrderDataHash(bytes32 dataHash);
event ValidateOrderDataHash(bytes32 dataHash);

struct ItemAmountMutation {
Expand Down Expand Up @@ -205,6 +208,7 @@ contract HashValidationZoneOfferer is

address internal _expectedOfferRecipient;

mapping(bytes32 => bytes32) public orderHashToAuthorizeOrderDataHash;
mapping(bytes32 => bytes32) public orderHashToValidateOrderDataHash;

// Pass in the null address to expect the fulfiller.
Expand All @@ -224,11 +228,36 @@ contract HashValidationZoneOfferer is
failureReasons[orderHash] = newFailureReason;
}

function authorizeOrder(ZoneParameters calldata)
function authorizeOrder(ZoneParameters calldata zoneParameters)
public
pure
returns (bytes4)
{
// Get the orderHash from zoneParameters
bytes32 orderHash = zoneParameters.orderHash;

// Get the length of msg.data
uint256 dataLength = msg.data.length;

// Create a variable to store msg.data in memory
bytes memory data;

// Copy msg.data to memory
assembly {
let ptr := mload(0x40)
calldatacopy(add(ptr, 0x20), 0, dataLength)
mstore(ptr, dataLength)
data := ptr
}

// Get the hash of msg.data
bytes32 calldataHash = keccak256(data);

// Store callDataHash in orderHashToAuthorizeOrderDataHash
orderHashToAuthorizeOrderDataHash[orderHash] = calldataHash;

// Emit a DataHash event with the hash of msg.data
emit AuthorizeOrderDataHash(calldataHash);

return this.authorizeOrder.selector;
}

Expand Down
88 changes: 80 additions & 8 deletions src/sol/lib/ZoneParametersLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,66 @@ library ZoneParametersLib {
bytes32[] orderHashes;
}

function getZoneParameters(
function getZoneAuthorizeParameters(
AdvancedOrder memory advancedOrder,
address fulfiller,
uint256 counter,
address seaport,
CriteriaResolver[] memory criteriaResolvers
) internal view returns (ZoneParameters memory zoneParameters) {
SeaportInterface seaportInterface = SeaportInterface(seaport);
// Get orderParameters from advancedOrder
OrderParameters memory orderParameters = advancedOrder.parameters;

// Get orderHash
bytes32 orderHash =
advancedOrder.getTipNeutralizedOrderHash(seaportInterface, counter);

(SpentItem[] memory spentItems, ReceivedItem[] memory receivedItems) =
orderParameters.getSpentAndReceivedItems(
advancedOrder.numerator,
advancedOrder.denominator,
0,
criteriaResolvers
);

// Create ZoneParameters and add to zoneParameters array
zoneParameters = ZoneParameters({
orderHash: orderHash,
fulfiller: fulfiller,
offerer: orderParameters.offerer,
offer: spentItems,
consideration: receivedItems,
extraData: advancedOrder.extraData,
orderHashes: new bytes32[](0),
startTime: orderParameters.startTime,
endTime: orderParameters.endTime,
zoneHash: orderParameters.zoneHash
});
}

function getZoneAuthorizeParameters(
AdvancedOrder[] memory advancedOrders,
address fulfiller,
uint256 maximumFulfilled,
address seaport,
CriteriaResolver[] memory criteriaResolvers,
UnavailableReason[] memory unavailableReasons
) internal view returns (ZoneParameters[] memory) {
return _getZoneParametersFromStruct(
_getZoneParametersStruct(
advancedOrders,
fulfiller,
maximumFulfilled,
seaport,
criteriaResolvers
),
unavailableReasons,
true
);
}

function getZoneValidateParameters(
AdvancedOrder memory advancedOrder,
address fulfiller,
uint256 counter,
Expand Down Expand Up @@ -107,14 +166,14 @@ library ZoneParametersLib {
offer: spentItems,
consideration: receivedItems,
extraData: advancedOrder.extraData,
orderHashes: orderHashes,
orderHashes: new bytes32[](0),
startTime: orderParameters.startTime,
endTime: orderParameters.endTime,
zoneHash: orderParameters.zoneHash
});
}

function getZoneParameters(
function getZoneValidateParameters(
AdvancedOrder[] memory advancedOrders,
address fulfiller,
uint256 maximumFulfilled,
Expand All @@ -130,7 +189,8 @@ library ZoneParametersLib {
seaport,
criteriaResolvers
),
unavailableReasons
unavailableReasons,
false
);
}

Expand All @@ -152,7 +212,8 @@ library ZoneParametersLib {

function _getZoneParametersFromStruct(
ZoneParametersStruct memory zoneParametersStruct,
UnavailableReason[] memory unavailableReasons
UnavailableReason[] memory unavailableReasons,
bool isAuthorize
) internal view returns (ZoneParameters[] memory) {
// TODO: use testHelpers pattern to use single amount deriver helper
ZoneDetails memory details = _getZoneDetails(zoneParametersStruct);
Expand All @@ -163,7 +224,7 @@ library ZoneParametersLib {
// Iterate over advanced orders to calculate orderHashes
_applyOrderHashes(details, zoneParametersStruct.seaport);

return _finalizeZoneParameters(details);
return _finalizeZoneParameters(details, isAuthorize);
}

function _getZoneDetails(ZoneParametersStruct memory zoneParametersStruct)
Expand Down Expand Up @@ -249,7 +310,7 @@ library ZoneParametersLib {
);
}

function _finalizeZoneParameters(ZoneDetails memory zoneDetails)
function _finalizeZoneParameters(ZoneDetails memory zoneDetails, bool isAuthorize)
internal
pure
returns (ZoneParameters[] memory zoneParameters)
Expand All @@ -266,14 +327,25 @@ library ZoneParametersLib {
break;
}

bytes32[] memory orderHashes;

if (isAuthorize) {
orderHashes = new bytes32[](i);
for (uint256 j = 0; j < i; j++) {
orderHashes[j] = zoneDetails.orderHashes[j];
}
} else {
orderHashes = zoneDetails.orderHashes;
}

if (zoneDetails.orderHashes[i] != bytes32(0)) {
// Create ZoneParameters and add to zoneParameters array
zoneParameters[i] = _createZoneParameters(
zoneDetails.orderHashes[i],
zoneDetails.orderDetails[i],
zoneDetails.advancedOrders[i],
zoneDetails.fulfiller,
zoneDetails.orderHashes
orderHashes
);
++totalFulfilled;
}
Expand Down
4 changes: 2 additions & 2 deletions test/foundry/new/FuzzEngine.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1464,8 +1464,8 @@ contract FuzzEngineTest is FuzzEngine {
considerationComponents
).withChecks(checks).withMaximumFulfilled(2);

context.expectations.expectedZoneCalldataHash = advancedOrders
.getExpectedZoneCalldataHash(
context.expectations.expectedZoneValidateCalldataHashes = advancedOrders
.getExpectedZoneValidateCalldataHash(
address(getSeaport()),
address(this),
new CriteriaResolver[](0),
Expand Down
9 changes: 5 additions & 4 deletions test/foundry/new/helpers/DebugUtil.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ struct ContextOutputSelection {
bool basicOrderParameters;
bool testHelpers;
bool checks;
bool expectedZoneCalldataHash;
bool expectedZoneAuthorizeCalldataHashes;
bool expectedZoneValidateCalldataHashes;
bool expectedContractOrderCalldataHashes;
bool expectedResults;
bool expectedImplicitExecutions;
Expand Down Expand Up @@ -216,11 +217,11 @@ function dumpContext(
cast(context.executionState.preExecOrderStatuses)
);
}
// if (outputSelection.expectedZoneCalldataHash) {
// if (outputSelection.expectedZoneValidateCalldataHash) {
// jsonOut = Searializer.tojsonDynArrayBytes32(
// "root",
// "expectedZoneCalldataHash",
// context.expectations.expectedZoneCalldataHash
// "expectedZoneValidateCalldataHash",
// context.expectations.expectedZoneValidateCalldataHash
// );
// }
// if (outputSelection.expectedContractOrderCalldataHashes) {
Expand Down
48 changes: 47 additions & 1 deletion test/foundry/new/helpers/FuzzChecks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,52 @@ abstract contract FuzzChecks is Test {
}
}

/**
* @dev Check that the zone is getting the right calldata in authorizeOrder.
*
* @param context A Fuzz test context.
*/
function check_authorizeOrderExpectedDataHash(FuzzTestContext memory context)
public
{
// Iterate over the orders.
for (uint256 i; i < context.executionState.orders.length; i++) {
OrderParameters memory order =
context.executionState.orders[i].parameters;


// If the order is restricted, check the calldata.
if (
order.orderType == OrderType.FULL_RESTRICTED
|| order.orderType == OrderType.PARTIAL_RESTRICTED
) {
testZone = payable(order.zone);

// Each order has a calldata hash, indexed to orders, that is
// expected to be returned by the zone.
bytes32 expectedCalldataHash =
context.expectations.expectedZoneAuthorizeCalldataHashes[i];

bytes32 orderHash =
context.executionState.orderDetails[i].orderHash;

// Use order hash to get the expected calldata hash from zone.
// TODO: fix this in cases where contract orders are part of
// orderHashes (the hash calculation is most likely incorrect).
bytes32 actualCalldataHash = HashValidationZoneOfferer(testZone)
.orderHashToAuthorizeOrderDataHash(orderHash);

// Check that the expected calldata hash matches the actual
// calldata hash.
assertEq(
actualCalldataHash,
expectedCalldataHash,
"check_authorizeOrderExpectedDataHash: actualCalldataHash != expectedCalldataHash"
);
}
}
}

/**
* @dev Check that the zone is getting the right calldata.
*
Expand All @@ -136,7 +182,7 @@ abstract contract FuzzChecks is Test {
// Each order has a calldata hash, indexed to orders, that is
// expected to be returned by the zone.
bytes32 expectedCalldataHash =
context.expectations.expectedZoneCalldataHash[i];
context.expectations.expectedZoneValidateCalldataHashes[i];

bytes32 orderHash =
context.executionState.orderDetails[i].orderHash;
Expand Down
44 changes: 42 additions & 2 deletions test/foundry/new/helpers/FuzzHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,46 @@ library FuzzHelpers {
}
}

/**
* @dev Derive ZoneParameters from a given restricted order and return
* the expected calldata hash for the call to authorizeOrder.
*
* @param orders The restricted orders.
* @param seaport The Seaport address.
* @param fulfiller The fulfiller.
* @param maximumFulfilled The maximum number of orders to fulfill.
* @param criteriaResolvers The criteria resolvers.
* @param maximumFulfilled The maximum number of orders to fulfill.
* @param unavailableReasons The availability status.
*
* @return calldataHashes The derived calldata hashes.
*/
function getExpectedZoneAuthorizeCalldataHash(
AdvancedOrder[] memory orders,
address seaport,
address fulfiller,
CriteriaResolver[] memory criteriaResolvers,
uint256 maximumFulfilled,
UnavailableReason[] memory unavailableReasons
) internal view returns (bytes32[] memory calldataHashes) {
calldataHashes = new bytes32[](orders.length);

ZoneParameters[] memory zoneParameters = orders.getZoneAuthorizeParameters(
fulfiller,
maximumFulfilled,
seaport,
criteriaResolvers,
unavailableReasons
);

for (uint256 i; i < zoneParameters.length; ++i) {
// Derive the expected calldata hash for the call to authorizeOrder
calldataHashes[i] = keccak256(
abi.encodeCall(ZoneInterface.authorizeOrder, (zoneParameters[i]))
);
}
}

/**
* @dev Derive ZoneParameters from a given restricted order and return
* the expected calldata hash for the call to validateOrder.
Expand All @@ -746,7 +786,7 @@ library FuzzHelpers {
*
* @return calldataHashes The derived calldata hashes.
*/
function getExpectedZoneCalldataHash(
function getExpectedZoneValidateCalldataHash(
AdvancedOrder[] memory orders,
address seaport,
address fulfiller,
Expand All @@ -756,7 +796,7 @@ library FuzzHelpers {
) internal view returns (bytes32[] memory calldataHashes) {
calldataHashes = new bytes32[](orders.length);

ZoneParameters[] memory zoneParameters = orders.getZoneParameters(
ZoneParameters[] memory zoneParameters = orders.getZoneValidateParameters(
fulfiller,
maximumFulfilled,
seaport,
Expand Down
Loading

0 comments on commit 192f6da

Please sign in to comment.