-
Notifications
You must be signed in to change notification settings - Fork 20
/
Verifiers.sol
270 lines (235 loc) · 10.6 KB
/
Verifiers.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {OrderStatus} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {Assertions} from "./Assertions.sol";
import {SignatureVerification} from "./SignatureVerification.sol";
import {
_revertInvalidTime,
_revertOrderAlreadyFilled,
_revertOrderIsCancelled,
_revertOrderPartiallyFilled
} from "seaport-types/src/lib/ConsiderationErrors.sol";
import {
BulkOrderProof_keyShift,
BulkOrderProof_keySize,
BulkOrderProof_lengthAdjustmentBeforeMask,
BulkOrderProof_lengthRangeAfterMask,
BulkOrderProof_minSize,
BulkOrderProof_rangeSize,
ECDSA_MaxLength,
OneWord,
OneWordShift,
ThirtyOneBytes,
TwoWords
} from "seaport-types/src/lib/ConsiderationConstants.sol";
/**
* @title Verifiers
* @author 0age
* @notice Verifiers contains functions for performing verifications.
*/
contract Verifiers is Assertions, SignatureVerification {
/**
* @dev Derive and set hashes, reference chainId, and associated domain
* separator during deployment.
*
* @param conduitController A contract that deploys conduits, or proxies
* that may optionally be used to transfer approved
* ERC20/721/1155 tokens.
*/
constructor(address conduitController) Assertions(conduitController) {}
/**
* @dev Internal view function to ensure that the current time falls within
* an order's valid timespan.
*
* @param startTime The time at which the order becomes active.
* @param endTime The time at which the order becomes inactive.
* @param revertOnInvalid A boolean indicating whether to revert if the
* order is not active.
*
* @return valid A boolean indicating whether the order is active.
*/
function _verifyTime(uint256 startTime, uint256 endTime, bool revertOnInvalid) internal view returns (bool valid) {
// Mark as valid if order has started and has not already ended.
assembly {
valid := and(iszero(gt(startTime, timestamp())), gt(endTime, timestamp()))
}
// Only revert on invalid if revertOnInvalid has been supplied as true.
if (revertOnInvalid && !valid) {
_revertInvalidTime(startTime, endTime);
}
}
/**
* @dev Internal view function to verify the signature of an order. An
* ERC-1271 fallback will be attempted if either the signature length
* is not 64 or 65 bytes or if the recovered signer does not match the
* supplied offerer. Note that in cases where a 64 or 65 byte signature
* is supplied, only standard ECDSA signatures that recover to a
* non-zero address are supported.
*
* @param offerer The offerer for the order.
* @param orderHash The order hash.
* @param signature A signature from the offerer indicating that the order
* has been approved.
*/
function _verifySignature(address offerer, bytes32 orderHash, bytes memory signature) internal view {
// Determine whether the offerer is the caller.
bool offererIsCaller;
assembly {
offererIsCaller := eq(offerer, caller())
}
// Skip signature verification if the offerer is the caller.
if (offererIsCaller) {
return;
}
// Derive the EIP-712 domain separator.
bytes32 domainSeparator = _domainSeparator();
// Derive original EIP-712 digest using domain separator and order hash.
bytes32 originalDigest = _deriveEIP712Digest(domainSeparator, orderHash);
// Read the length of the signature from memory and place on the stack.
uint256 originalSignatureLength = signature.length;
// Determine effective digest if signature has a valid bulk order size.
bytes32 digest;
if (_isValidBulkOrderSize(originalSignatureLength)) {
// Rederive order hash and digest using bulk order proof.
(orderHash) = _computeBulkOrderProof(signature, orderHash);
digest = _deriveEIP712Digest(domainSeparator, orderHash);
} else {
// Supply the original digest as the effective digest.
digest = originalDigest;
}
// Ensure that the signature for the digest is valid for the offerer.
_assertValidSignature(offerer, digest, originalDigest, originalSignatureLength, signature);
}
/**
* @dev Determines whether the specified bulk order size is valid.
*
* @param signatureLength The signature length of the bulk order to check.
*
* @return validLength True if bulk order size is valid, false otherwise.
*/
function _isValidBulkOrderSize(uint256 signatureLength) internal pure returns (bool validLength) {
// Utilize assembly to validate the length; the equivalent logic is
// (64 + x) + 3 + 32y where (0 <= x <= 1) and (1 <= y <= 24).
assembly {
validLength :=
and(
lt(sub(signatureLength, BulkOrderProof_minSize), BulkOrderProof_rangeSize),
lt(
and(add(signatureLength, BulkOrderProof_lengthAdjustmentBeforeMask), ThirtyOneBytes),
BulkOrderProof_lengthRangeAfterMask
)
)
}
}
/**
* @dev Computes the bulk order hash for the specified proof and leaf. Note
* that if an index that exceeds the number of orders in the bulk order
* payload will instead "wrap around" and refer to an earlier index.
*
* @param proofAndSignature The proof and signature of the bulk order.
* @param leaf The leaf of the bulk order tree.
*
* @return bulkOrderHash The bulk order hash.
*/
function _computeBulkOrderProof(bytes memory proofAndSignature, bytes32 leaf)
internal
pure
returns (bytes32 bulkOrderHash)
{
// Declare arguments for the root hash and the height of the proof.
bytes32 root;
uint256 height;
// Utilize assembly to efficiently derive the root hash using the proof.
assembly {
// Retrieve the length of the proof, key, and signature combined.
let fullLength := mload(proofAndSignature)
// If proofAndSignature has odd length, it is a compact signature
// with 64 bytes.
let signatureLength := sub(ECDSA_MaxLength, and(fullLength, 1))
// Derive height (or depth of tree) with signature and proof length.
height := shr(OneWordShift, sub(fullLength, signatureLength))
// Update the length in memory to only include the signature.
mstore(proofAndSignature, signatureLength)
// Derive the pointer for the key using the signature length.
let keyPtr := add(proofAndSignature, add(OneWord, signatureLength))
// Retrieve the three-byte key using the derived pointer.
let key := shr(BulkOrderProof_keyShift, mload(keyPtr))
/// Retrieve pointer to first proof element by applying a constant
// for the key size to the derived key pointer.
let proof := add(keyPtr, BulkOrderProof_keySize)
// Compute level 1.
let scratchPtr1 := shl(OneWordShift, and(key, 1))
mstore(scratchPtr1, leaf)
mstore(xor(scratchPtr1, OneWord), mload(proof))
// Compute remaining proofs.
for { let i := 1 } lt(i, height) { i := add(i, 1) } {
proof := add(proof, OneWord)
let scratchPtr := shl(OneWordShift, and(shr(i, key), 1))
mstore(scratchPtr, keccak256(0, TwoWords))
mstore(xor(scratchPtr, OneWord), mload(proof))
}
// Compute root hash.
root := keccak256(0, TwoWords)
}
// Retrieve appropriate typehash constant based on height.
bytes32 rootTypeHash = _lookupBulkOrderTypehash(height);
// Use the typehash and the root hash to derive final bulk order hash.
assembly {
mstore(0, rootTypeHash)
mstore(OneWord, root)
bulkOrderHash := keccak256(0, TwoWords)
}
}
/**
* @dev Internal view function to validate that a given order is fillable
* and not cancelled based on the order status.
*
* @param orderHash The order hash.
* @param orderStatus The status of the order, including whether it has
* been cancelled and the fraction filled.
* @param onlyAllowUnused A boolean flag indicating whether partial fills
* are supported by the calling function.
* @param revertOnInvalid A boolean indicating whether to revert if the
* order has been cancelled or filled beyond the
* allowable amount.
*
* @return valid A boolean indicating whether the order is valid.
*/
function _verifyOrderStatus(
bytes32 orderHash,
OrderStatus storage orderStatus,
bool onlyAllowUnused,
bool revertOnInvalid
) internal view returns (bool valid) {
// Ensure that the order has not been cancelled.
if (orderStatus.isCancelled) {
// Only revert if revertOnInvalid has been supplied as true.
if (revertOnInvalid) {
_revertOrderIsCancelled(orderHash);
}
// Return false as the order status is invalid.
return false;
}
// Read order status numerator from storage and place on stack.
uint256 orderStatusNumerator = orderStatus.numerator;
// If the order is not entirely unused...
if (orderStatusNumerator != 0) {
// ensure the order has not been partially filled when not allowed.
if (onlyAllowUnused) {
// Always revert on partial fills when onlyAllowUnused is true.
_revertOrderPartiallyFilled(orderHash);
}
// Otherwise, ensure that order has not been entirely filled.
else if (orderStatusNumerator >= orderStatus.denominator) {
// Only revert if revertOnInvalid has been supplied as true.
if (revertOnInvalid) {
_revertOrderAlreadyFilled(orderHash);
}
// Return false as the order status is invalid.
return false;
}
}
// Return true as the order status is valid.
valid = true;
}
}