|
| 1 | +// SPDX-License-Identifier: AGPL-3.0 |
| 2 | + |
| 3 | +pragma solidity ^0.6.12; |
| 4 | + |
| 5 | +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; |
| 6 | + |
| 7 | +/** |
| 8 | + * @dev Rollup helper functions |
| 9 | + */ |
| 10 | +contract HermezHelpersV2 is Initializable { |
| 11 | + uint256 private constant _WORD_SIZE = 32; |
| 12 | + |
| 13 | + // bytes32 public constant EIP712DOMAIN_HASH = |
| 14 | + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") |
| 15 | + bytes32 |
| 16 | + public constant EIP712DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; |
| 17 | + // bytes32 public constant NAME_HASH = |
| 18 | + // keccak256("Hermez Network") |
| 19 | + bytes32 |
| 20 | + public constant NAME_HASH = 0xbe287413178bfeddef8d9753ad4be825ae998706a6dabff23978b59dccaea0ad; |
| 21 | + // bytes32 public constant VERSION_HASH = |
| 22 | + // keccak256("1") |
| 23 | + bytes32 |
| 24 | + public constant VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; |
| 25 | + // bytes32 public constant AUTHORISE_TYPEHASH = |
| 26 | + // keccak256("Authorise(string Provider,string Authorisation,bytes32 BJJKey)"); |
| 27 | + bytes32 |
| 28 | + public constant AUTHORISE_TYPEHASH = 0xafd642c6a37a2e6887dc4ad5142f84197828a904e53d3204ecb1100329231eaa; |
| 29 | + // bytes32 public constant HERMEZ_NETWORK_HASH = keccak256(bytes("Hermez Network")), |
| 30 | + bytes32 |
| 31 | + public constant HERMEZ_NETWORK_HASH = 0xbe287413178bfeddef8d9753ad4be825ae998706a6dabff23978b59dccaea0ad; |
| 32 | + // bytes32 public constant ACCOUNT_CREATION_HASH = keccak256(bytes("Account creation")), |
| 33 | + bytes32 |
| 34 | + public constant ACCOUNT_CREATION_HASH = 0xff946cf82975b1a2b6e6d28c9a76a4b8d7a1fd0592b785cb92771933310f9ee7; |
| 35 | + |
| 36 | + /** |
| 37 | + * @dev Decode half floating precision. |
| 38 | + * Max value encoded with this codification: 0x1f8def8800cca870c773f6eb4d980000000 (aprox 137 bits) |
| 39 | + * @param float Float half precision encode number |
| 40 | + * @return Decoded floating half precision |
| 41 | + */ |
| 42 | + function _float2Fix(uint40 float) internal pure returns (uint256) { |
| 43 | + uint256 m = float & 0x7FFFFFFFF; |
| 44 | + uint256 e = float >> 35; |
| 45 | + |
| 46 | + // never overflow, max "e" value is 32 |
| 47 | + uint256 exp = 10**e; |
| 48 | + |
| 49 | + // never overflow, max "fix" value is 1023 * 10^32 |
| 50 | + uint256 fix = m * exp; |
| 51 | + |
| 52 | + return fix; |
| 53 | + } |
| 54 | + |
| 55 | + /** |
| 56 | + * @dev Retrieve the DOMAIN_SEPARATOR hash |
| 57 | + * @return domainSeparator hash used for sign messages |
| 58 | + */ |
| 59 | + function DOMAIN_SEPARATOR() public view returns (bytes32 domainSeparator) { |
| 60 | + return |
| 61 | + keccak256( |
| 62 | + abi.encode( |
| 63 | + EIP712DOMAIN_HASH, |
| 64 | + NAME_HASH, |
| 65 | + VERSION_HASH, |
| 66 | + getChainId(), |
| 67 | + address(this) |
| 68 | + ) |
| 69 | + ); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * @return chainId The current chainId where the smarctoncract is executed |
| 74 | + */ |
| 75 | + function getChainId() public pure returns (uint256 chainId) { |
| 76 | + assembly { |
| 77 | + chainId := chainid() |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * @dev Retrieve ethereum address from a (defaultMessage + babyjub) signature |
| 83 | + * @param babyjub Public key babyjubjub represented as point: sign + (Ay) |
| 84 | + * @param r Signature parameter |
| 85 | + * @param s Signature parameter |
| 86 | + * @param v Signature parameter |
| 87 | + * @return Ethereum address recovered from the signature |
| 88 | + */ |
| 89 | + function _checkSig( |
| 90 | + bytes32 babyjub, |
| 91 | + bytes32 r, |
| 92 | + bytes32 s, |
| 93 | + uint8 v |
| 94 | + ) internal view returns (address) { |
| 95 | + // from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol#L46 |
| 96 | + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature |
| 97 | + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines |
| 98 | + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most |
| 99 | + // signatures from current libraries generate a unique signature with an s-value in the lower half order. |
| 100 | + // |
| 101 | + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value |
| 102 | + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or |
| 103 | + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept |
| 104 | + // these malleable signatures as well. |
| 105 | + require( |
| 106 | + uint256(s) <= |
| 107 | + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, |
| 108 | + "HermezHelpers::_checkSig: INVALID_S_VALUE" |
| 109 | + ); |
| 110 | + |
| 111 | + bytes32 encodeData = keccak256( |
| 112 | + abi.encode( |
| 113 | + AUTHORISE_TYPEHASH, |
| 114 | + HERMEZ_NETWORK_HASH, |
| 115 | + ACCOUNT_CREATION_HASH, |
| 116 | + babyjub |
| 117 | + ) |
| 118 | + ); |
| 119 | + |
| 120 | + bytes32 messageDigest = keccak256( |
| 121 | + abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), encodeData) |
| 122 | + ); |
| 123 | + |
| 124 | + address ethAddress = ecrecover(messageDigest, v, r, s); |
| 125 | + |
| 126 | + require( |
| 127 | + ethAddress != address(0), |
| 128 | + "HermezHelpers::_checkSig: INVALID_SIGNATURE" |
| 129 | + ); |
| 130 | + |
| 131 | + return ethAddress; |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * @dev return information from specific call data info |
| 136 | + * @param posParam parameter number relative to 0 to extract the info |
| 137 | + * @return ptr ptr to the call data position where the actual data starts |
| 138 | + * @return len Length of the data |
| 139 | + */ |
| 140 | + function _getCallData(uint256 posParam) |
| 141 | + internal |
| 142 | + pure |
| 143 | + returns (uint256 ptr, uint256 len) |
| 144 | + { |
| 145 | + assembly { |
| 146 | + let pos := add(4, mul(posParam, 32)) |
| 147 | + ptr := add(calldataload(pos), 4) |
| 148 | + len := calldataload(ptr) |
| 149 | + ptr := add(ptr, 32) |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + /** |
| 154 | + * @dev This package fills at least len zeros in memory and a maximum of len+31 |
| 155 | + * @param ptr The position where it starts to fill zeros |
| 156 | + * @param len The minimum quantity of zeros it's added |
| 157 | + */ |
| 158 | + function _fillZeros(uint256 ptr, uint256 len) internal pure { |
| 159 | + assembly { |
| 160 | + let ptrTo := ptr |
| 161 | + ptr := add(ptr, len) |
| 162 | + for { |
| 163 | + |
| 164 | + } lt(ptrTo, ptr) { |
| 165 | + ptrTo := add(ptrTo, 32) |
| 166 | + } { |
| 167 | + mstore(ptrTo, 0) |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + /** |
| 173 | + * @dev Copy 'len' bytes from memory address 'src', to address 'dest'. |
| 174 | + * From https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol |
| 175 | + * @param _preBytes bytes storage |
| 176 | + * @param _postBytes Bytes array memory |
| 177 | + */ |
| 178 | + function _concatStorage(bytes storage _preBytes, bytes memory _postBytes) |
| 179 | + internal |
| 180 | + { |
| 181 | + assembly { |
| 182 | + // Read the first 32 bytes of _preBytes storage, which is the length |
| 183 | + // of the array. (We don't need to use the offset into the slot |
| 184 | + // because arrays use the entire slot.) |
| 185 | + let fslot := sload(_preBytes_slot) |
| 186 | + // Arrays of 31 bytes or less have an even value in their slot, |
| 187 | + // while longer arrays have an odd value. The actual length is |
| 188 | + // the slot divided by two for odd values, and the lowest order |
| 189 | + // byte divided by two for even values. |
| 190 | + // If the slot is even, bitwise and the slot with 255 and divide by |
| 191 | + // two to get the length. If the slot is odd, bitwise and the slot |
| 192 | + // with -1 and divide by two. |
| 193 | + let slength := div( |
| 194 | + and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), |
| 195 | + 2 |
| 196 | + ) |
| 197 | + let mlength := mload(_postBytes) |
| 198 | + let newlength := add(slength, mlength) |
| 199 | + // slength can contain both the length and contents of the array |
| 200 | + // if length < 32 bytes so let's prepare for that |
| 201 | + // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage |
| 202 | + switch add(lt(slength, 32), lt(newlength, 32)) |
| 203 | + case 2 { |
| 204 | + // Since the new array still fits in the slot, we just need to |
| 205 | + // update the contents of the slot. |
| 206 | + // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length |
| 207 | + sstore( |
| 208 | + _preBytes_slot, |
| 209 | + // all the modifications to the slot are inside this |
| 210 | + // next block |
| 211 | + add( |
| 212 | + // we can just add to the slot contents because the |
| 213 | + // bytes we want to change are the LSBs |
| 214 | + fslot, |
| 215 | + add( |
| 216 | + mul( |
| 217 | + div( |
| 218 | + // load the bytes from memory |
| 219 | + mload(add(_postBytes, 0x20)), |
| 220 | + // zero all bytes to the right |
| 221 | + exp(0x100, sub(32, mlength)) |
| 222 | + ), |
| 223 | + // and now shift left the number of bytes to |
| 224 | + // leave space for the length in the slot |
| 225 | + exp(0x100, sub(32, newlength)) |
| 226 | + ), |
| 227 | + // increase length by the double of the memory |
| 228 | + // bytes length |
| 229 | + mul(mlength, 2) |
| 230 | + ) |
| 231 | + ) |
| 232 | + ) |
| 233 | + } |
| 234 | + case 1 { |
| 235 | + // The stored value fits in the slot, but the combined value |
| 236 | + // will exceed it. |
| 237 | + // get the keccak hash to get the contents of the array |
| 238 | + mstore(0x0, _preBytes_slot) |
| 239 | + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) |
| 240 | + |
| 241 | + // save new length |
| 242 | + sstore(_preBytes_slot, add(mul(newlength, 2), 1)) |
| 243 | + |
| 244 | + // The contents of the _postBytes array start 32 bytes into |
| 245 | + // the structure. Our first read should obtain the `submod` |
| 246 | + // bytes that can fit into the unused space in the last word |
| 247 | + // of the stored array. To get this, we read 32 bytes starting |
| 248 | + // from `submod`, so the data we read overlaps with the array |
| 249 | + // contents by `submod` bytes. Masking the lowest-order |
| 250 | + // `submod` bytes allows us to add that value directly to the |
| 251 | + // stored value. |
| 252 | + |
| 253 | + let submod := sub(32, slength) |
| 254 | + let mc := add(_postBytes, submod) |
| 255 | + let end := add(_postBytes, mlength) |
| 256 | + let mask := sub(exp(0x100, submod), 1) |
| 257 | + |
| 258 | + sstore( |
| 259 | + sc, |
| 260 | + add( |
| 261 | + and( |
| 262 | + fslot, |
| 263 | + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 |
| 264 | + ), |
| 265 | + and(mload(mc), mask) |
| 266 | + ) |
| 267 | + ) |
| 268 | + |
| 269 | + for { |
| 270 | + mc := add(mc, 0x20) |
| 271 | + sc := add(sc, 1) |
| 272 | + } lt(mc, end) { |
| 273 | + sc := add(sc, 1) |
| 274 | + mc := add(mc, 0x20) |
| 275 | + } { |
| 276 | + sstore(sc, mload(mc)) |
| 277 | + } |
| 278 | + |
| 279 | + mask := exp(0x100, sub(mc, end)) |
| 280 | + |
| 281 | + sstore(sc, mul(div(mload(mc), mask), mask)) |
| 282 | + } |
| 283 | + default { |
| 284 | + // get the keccak hash to get the contents of the array |
| 285 | + mstore(0x0, _preBytes_slot) |
| 286 | + // Start copying to the last used word of the stored array. |
| 287 | + let sc := add(keccak256(0x0, 0x20), div(slength, 32)) |
| 288 | + |
| 289 | + // save new length |
| 290 | + sstore(_preBytes_slot, add(mul(newlength, 2), 1)) |
| 291 | + |
| 292 | + // Copy over the first `submod` bytes of the new data as in |
| 293 | + // case 1 above. |
| 294 | + let slengthmod := mod(slength, 32) |
| 295 | + let mlengthmod := mod(mlength, 32) |
| 296 | + let submod := sub(32, slengthmod) |
| 297 | + let mc := add(_postBytes, submod) |
| 298 | + let end := add(_postBytes, mlength) |
| 299 | + let mask := sub(exp(0x100, submod), 1) |
| 300 | + |
| 301 | + sstore(sc, add(sload(sc), and(mload(mc), mask))) |
| 302 | + |
| 303 | + for { |
| 304 | + sc := add(sc, 1) |
| 305 | + mc := add(mc, 0x20) |
| 306 | + } lt(mc, end) { |
| 307 | + sc := add(sc, 1) |
| 308 | + mc := add(mc, 0x20) |
| 309 | + } { |
| 310 | + sstore(sc, mload(mc)) |
| 311 | + } |
| 312 | + |
| 313 | + mask := exp(0x100, sub(mc, end)) |
| 314 | + |
| 315 | + sstore(sc, mul(div(mload(mc), mask), mask)) |
| 316 | + } |
| 317 | + } |
| 318 | + } |
| 319 | +} |
0 commit comments