├── .gitattribute ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .npmignore ├── .nvmrc ├── .soliumignore ├── .soliumrc.json ├── LICENSE ├── README.md ├── contracts ├── AssertBytes.sol └── BytesLib.sol ├── ethpm.json ├── foundry.toml ├── funding.json ├── package-lock.json ├── package.json └── test ├── TestBytesLib1.sol └── TestBytesLib2.sol /.gitattribute: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System Cruft 2 | # ================================================ 3 | .DS_Store 4 | *.swp 5 | *.swo 6 | 7 | # secret stuff 8 | # ================================================ 9 | secrets.json 10 | 11 | # Generated stuff 12 | # ================================================ 13 | build/* 14 | node_modules/* 15 | /cache 16 | /out 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # secret stuff 2 | # ================================================ 3 | secrets.json 4 | 5 | # Generated stuff 6 | # ================================================ 7 | build/* 8 | node_modules/* 9 | 10 | # Unneeded files for NPM deployment 11 | # ================================================ 12 | truffle.js 13 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.16.0 -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverageEnv 3 | .git 4 | test 5 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": ["security"], 4 | "rules": { 5 | "error-reason": "off", 6 | "indentation": ["error", 4], 7 | "lbrace": "off", 8 | "linebreak-style": ["error", "unix"], 9 | "no-constant": ["error"], 10 | "no-empty-blocks": "warning", 11 | "quotes": ["error", "double"], 12 | "uppercase": "off", 13 | "imports-on-top": "error", 14 | "visibility-first": "error", 15 | "security/enforce-explicit-visibility": ["error"], 16 | "security/no-block-members": ["off"], 17 | "security/no-inline-assembly": ["off"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solidity Bytes Arrays Utils 2 | 3 | Bytes tightly packed arrays' utility library for ethereum contracts written in Solidity. 4 | 5 | The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. 6 | 7 | Given this library has an all-internal collection of methods it doesn't make sense to have it reside in the mainnet. Instead it will only be available on EPM as an installable package. 8 | 9 | ## Important Fixes Changelog 10 | 11 | _**2021-01-07**_ 12 | 13 | A bug regarding zero-length slices was disclosed by @MrChico following an audit to the Optimism codebase. 14 | 15 | The exact bug happened under the following conditions: if memory slots higher then the current free-memory pointer were tainted before calling the `slice` method with a desired length of `0`, the returned bytes array, instead of being a zero-length slice was an array of arbitrary length based on the values that previously populated that memory region. 16 | 17 | Overall, the usage of zero-length slices should be pretty unusual and, as such, hopefully, this bug does not have far-reaching implications. Nonetheless, *please update the library to the new version if you're using it in production*. 18 | 19 | **TL;DR: if you're using the `slice` method with a length parameter of '0' in your codebase, please update to version 0.1.2 of the bytes library ASAP!** 20 | 21 | _**2020-11-01**_ 22 | 23 | There was a **critical bug** in the `slice` method, reported on an audit to a DXDao codebase. 24 | 25 | Previously, no checks were being made on overflows of the `_start` and `_length` parameters since previous reviews of the codebase deemed this overflow "unexploitable" because of an inordinate expansion of memory (i.e., reading an immensely large memory offset causing huge memory expansion) resulting in an out-of-gas exception. 26 | 27 | However, as noted in the review mentioned above, this is not the case. The `slice` method in versions `<=0.9.0` actually allows for arbitrary _kind of_ (i.e., it allows memory writes to very specific values) arbitrary memory writes _in the specific case where these parameters are user-supplied inputs and not hardcoded values (which is uncommon). 28 | 29 | This made me realize that in permissioned blockchains where gas is also not a limiting factor this could become problematic in other methods and so I updated all typecasting-related methods to include new bound checks as well. 30 | 31 | **TL;DR: if you're using the `slice` method with user-supplied inputs in your codebase please update the bytes library immediately!** 32 | 33 | ## _Version Notes_: 34 | 35 | * Version `v0.9.0` has a new feature: a new "equal_nonAligned" method that allows for comparing two bytes arrays that are not aligned to 32 bytes. 36 | This is useful for comparing bytes arrays that were created with assembly/Yul or other, non-Solidity compilers that don't pad bytes arrays to 32 bytes. 37 | 38 | * Starting from version `v0.8.0` the versioning will change to follow compatible Solidity's compiler versions. 39 | This means that now the library will only compile on Solidity versions `>=0.8.0` so, if you need `<0.8.0` support for your project just use `v0.1.2` of the library with: 40 | 41 | ``` 42 | $ truffle install bytes@0.8.0 43 | ``` 44 | or 45 | ``` 46 | $ npm install solidity-bytes-utils@0.8.0 47 | ``` 48 | 49 | * Version `v0.1.2` has a major bug fix. 50 | 51 | * Version `v0.1.1` has a critical bug fix. 52 | 53 | * Version `v0.9.0` now compiles with Solidity compilers `0.5.x` and `0.6.x`. 54 | 55 | * Since version `v0.0.7` the library will only compile on Solidity versions `>0.4.22` so, if you need `v0.4.x` support for your project just use `v0.0.6` of the library with: 56 | 57 | ``` 58 | $ truffle install bytes@0.0.6 59 | ``` 60 | or 61 | ``` 62 | $ npm install solidity-bytes-utils@0.0.6 63 | ``` 64 | 65 | ## Usage 66 | 67 | You can use the library here present by direct download and importing with: 68 | ``` 69 | import "BytesLib.sol"; 70 | ``` 71 | 72 | or, if you have installed it from EPM (see below), with Truffle's specific paths: 73 | ``` 74 | import "bytes/BytesLib.sol"; 75 | ``` 76 | 77 | Usage examples and API are more thoroughly explained below. 78 | 79 | Also there's an extra library in there called `AssertBytes` (inside the same named file) which is compatible with Truffle's Solidity testing library `Assert.sol` event firing and so lets you now test bytes equalities/inequalities in your Solidity tests by just importing it in your `.sol` test files: 80 | ``` 81 | import "bytes/AssertBytes.sol"; 82 | ``` 83 | 84 | and use the library `AssertBytes` much like they use `Assert` in Truffle's [example](http://truffleframework.com/docs/getting_started/solidity-tests). 85 | 86 | ## EthPM 87 | 88 | This library is published in EPM under the alias `bytes` 89 | 90 | **Installing it with Truffle** 91 | 92 | ``` 93 | $ truffle install bytes 94 | ``` 95 | 96 | ## NPM 97 | 98 | This library is published in NPM under the alias `solidity-bytes-utils` 99 | 100 | **Installing it with NPM** 101 | 102 | ``` 103 | $ npm install solidity-bytes-utils 104 | ``` 105 | 106 | **Importing it in your Solidity contract** 107 | 108 | ``` 109 | import "solidity-bytes-utils/contracts/BytesLib.sol"; 110 | ``` 111 | 112 | ## Contributing 113 | 114 | Contributions are more than welcome in any way shape or form! 😄 115 | 116 | TODOs: 117 | * Two storage bytes arrays concatenation 118 | * Slicing directly from storage 119 | * Implement inline assembly functions for better readability 120 | 121 | ### Testing 122 | 123 | This project uses Truffle for tests. Truffle's version of `solc` needs to be at least 0.4.19 for the contracts to compile. If you encounter compilation errors, try: 124 | 125 | $ cd /usr/local/lib/node_modules/truffle 126 | $ npm install solc@latest 127 | 128 | To run the tests, start a `testrpc` instance, then run `truffle test`. 129 | 130 | ## API 131 | 132 | * `function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes)` 133 | 134 | Concatenates two `bytes` arrays in memory and returns the concatenation result as another `bytes` array in memory. 135 | 136 | 137 | * `function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal pure` 138 | 139 | Concatenates a `bytes` array present in memory directly into the given storage location addressed by the `_preBytes` storage pointer. 140 | 141 | 142 | * `function slice(bytes _bytes, uint _start, uint _length) internal pure returns (bytes)` 143 | 144 | Takes a slice from a `bytes` array in memory of given `length` starting from `_start`th byte counting from the left-most one (0-based). 145 | 146 | 147 | * `function toAddress(bytes _bytes, uint _start) internal pure returns (address)` 148 | 149 | Takes a 20-byte-long sequence present in a `bytes` array in memory and returns that as an address (also checks for sufficient length). 150 | 151 | 152 | * `function toUint(bytes _bytes, uint _start) internal pure returns (uint256)` 153 | 154 | Takes a 32-byte-long sequence present in a `bytes` array in memory and returns that as an unsigned integer (also checks for sufficient length). 155 | 156 | 157 | * `function equal(bytes memory _preBytes, bytes memory _postBytes) internal view returns (bool)` 158 | 159 | Compares two `bytes` arrays in memory and returns the comparison result as a `bool` variable. 160 | 161 | 162 | * `function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool)` 163 | 164 | Compares a `bytes` array in storage against another `bytes` array in memory and returns the comparison result as a `bool` variable. 165 | 166 | 167 | ## Examples 168 | 169 | Ordered to mimic the above `API` section ordering: 170 | 171 | ```javascript 172 | contract MyContract { 173 | using BytesLib for bytes; 174 | 175 | function myFunc() { 176 | bytes memory _preBytes = hex"f00dfeed"; 177 | bytes memory _postBytes = hex"f00dfeed"; 178 | 179 | bytes memory concatBytes = _preBytes.concat(_postBytes); 180 | 181 | // concatBytes == 0xf00dfeedf00dfeed 182 | } 183 | } 184 | ``` 185 | 186 | 187 | ```javascript 188 | contract MyContract { 189 | using BytesLib for bytes; 190 | 191 | bytes storageBytes = hex"f00dfeed"; 192 | 193 | function myFunc() { 194 | bytes memory _postBytes = hex"f00dfeed"; 195 | 196 | storageBytes.concatStorage(_postBytes); 197 | 198 | // storageBytes == 0xf00dfeedf00dfeed 199 | } 200 | } 201 | ``` 202 | 203 | 204 | ```javascript 205 | contract MyContract { 206 | using BytesLib for bytes; 207 | 208 | function myFunc() { 209 | bytes memory memBytes = hex"f00dfeedaabbccddeeff"; 210 | 211 | bytes memory slice1 = memBytes.slice(0, 2); 212 | bytes memory slice2 = memBytes.slice(2, 2); 213 | 214 | // slice1 == 0xf00d 215 | // slice2 == 0xfeed 216 | } 217 | } 218 | ``` 219 | 220 | 221 | ```javascript 222 | contract MyContract { 223 | using BytesLib for bytes; 224 | 225 | function myFunc() { 226 | bytes memory memBytes = hex"f00dfeed383Fa3B60f9B4AB7fBf6835d3c26C3765cD2B2e2f00dfeed"; 227 | 228 | address addrFromBytes = memBytes.toAddress(4); 229 | 230 | // addrFromBytes == 0x383Fa3B60f9B4AB7fBf6835d3c26C3765cD2B2e2 231 | } 232 | } 233 | ``` 234 | 235 | 236 | ```javascript 237 | contract MyContract { 238 | using BytesLib for bytes; 239 | 240 | function myFunc() { 241 | bytes memory memBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000042feed"; 242 | 243 | uint256 uintFromBytes = memBytes.toUint(2); 244 | 245 | // uintFromBytes == 42 246 | } 247 | } 248 | ``` 249 | 250 | 251 | ```javascript 252 | contract MyContract { 253 | using BytesLib for bytes; 254 | 255 | function myFunc() { 256 | bytes memory memBytes = hex"f00dfeed"; 257 | bytes memory checkBytesTrue = hex"f00dfeed"; 258 | bytes memory checkBytesFalse = hex"00000000"; 259 | 260 | bool check1 = memBytes.equal(checkBytesTrue); 261 | bool check2 = memBytes.equal(checkBytesFalse); 262 | 263 | // check1 == true 264 | // check2 == false 265 | } 266 | } 267 | ``` 268 | 269 | 270 | ```javascript 271 | 272 | contract MyContract { 273 | using BytesLib for bytes; 274 | 275 | bytes storageBytes = hex"f00dfeed"; 276 | 277 | function myFunc() { 278 | bytes memory checkBytesTrue = hex"f00dfeed"; 279 | bytes memory checkBytesFalse = hex"00000000"; 280 | 281 | bool check1 = storageBytes.equalStorage(checkBytesTrue); 282 | bool check2 = storageBytes.equalStorage(checkBytesFalse); 283 | 284 | // check1 == true 285 | // check2 == false 286 | } 287 | } 288 | ``` 289 | -------------------------------------------------------------------------------- /contracts/AssertBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | /* 3 | * @title Solidity Bytes Assertion Library 4 | * @author Gonçalo Sá 5 | * 6 | * @dev A Solidity library built to complete assertions in Solidity unit tests. 7 | * This library is compliant with the test event convention that the Truffle suite uses. 8 | */ 9 | 10 | pragma solidity >=0.8.0 <0.9.0; 11 | 12 | 13 | library AssertBytes { 14 | // Event to maintain compatibility with Truffle's Assertion Lib 15 | event TestEvent(bool indexed result, string message); 16 | 17 | /* 18 | Function: equal(bytes memory, bytes memory) 19 | 20 | Assert that two tightly packed bytes arrays are equal. 21 | 22 | Params: 23 | A (bytes) - The first bytes. 24 | B (bytes) - The second bytes. 25 | message (string) - A message that is sent if the assertion fails. 26 | 27 | Returns: 28 | result (bool) - The result. 29 | */ 30 | function _equal(bytes memory _a, bytes memory _b) internal pure returns (bool) { 31 | bool returnBool = true; 32 | 33 | assembly { 34 | let length := mload(_a) 35 | 36 | // if lengths don't match the arrays are not equal 37 | switch eq(length, mload(_b)) 38 | case 1 { 39 | // cb is a circuit breaker in the for loop since there's 40 | // no said feature for inline assembly loops 41 | // cb = 1 - don't breaker 42 | // cb = 0 - break 43 | let cb := 1 44 | 45 | let mc := add(_a, 0x20) 46 | let end := add(mc, length) 47 | 48 | for { 49 | let cc := add(_b, 0x20) 50 | // the next line is the loop condition: 51 | // while(uint256(mc < end) + cb == 2) 52 | } eq(add(lt(mc, end), cb), 2) { 53 | mc := add(mc, 0x20) 54 | cc := add(cc, 0x20) 55 | } { 56 | // if any of these checks fails then arrays are not equal 57 | if iszero(eq(mload(mc), mload(cc))) { 58 | // unsuccess: 59 | returnBool := 0 60 | cb := 0 61 | } 62 | } 63 | } 64 | default { 65 | // unsuccess: 66 | returnBool := 0 67 | } 68 | } 69 | 70 | return returnBool; 71 | } 72 | 73 | function equal(bytes memory _a, bytes memory _b, string memory message) internal returns (bool) { 74 | bool returnBool = _equal(_a, _b); 75 | 76 | _report(returnBool, message); 77 | 78 | return returnBool; 79 | } 80 | 81 | function notEqual(bytes memory _a, bytes memory _b, string memory message) internal returns (bool) { 82 | bool returnBool = _equal(_a, _b); 83 | 84 | _report(!returnBool, message); 85 | 86 | return !returnBool; 87 | } 88 | 89 | /* 90 | Function: equal_nonAligned(bytes memory, bytes memory) 91 | 92 | Assert that two tightly packed bytes arrays that are not aligned to 32 bytes are equal. 93 | 94 | Params: 95 | A (bytes) - The first bytes. 96 | B (bytes) - The second bytes. 97 | message (string) - A message that is sent if the assertion fails. 98 | 99 | Returns: 100 | result (bool) - The result. 101 | */ 102 | 103 | 104 | function _equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { 105 | bool success = true; 106 | 107 | assembly { 108 | let length := mload(_preBytes) 109 | 110 | // if lengths don't match the arrays are not equal 111 | switch eq(length, mload(_postBytes)) 112 | case 1 { 113 | // cb is a circuit breaker in the for loop since there's 114 | // no said feature for inline assembly loops 115 | // cb = 1 - don't breaker 116 | // cb = 0 - break 117 | let cb := 1 118 | 119 | let endMinusWord := add(_preBytes, length) 120 | let mc := add(_preBytes, 0x20) 121 | let cc := add(_postBytes, 0x20) 122 | 123 | for { 124 | // the next line is the loop condition: 125 | // while(uint256(mc < endWord) + cb == 2) 126 | } eq(add(lt(mc, endMinusWord), cb), 2) { 127 | mc := add(mc, 0x20) 128 | cc := add(cc, 0x20) 129 | } { 130 | // if any of these checks fails then arrays are not equal 131 | if iszero(eq(mload(mc), mload(cc))) { 132 | // unsuccess: 133 | success := 0 134 | cb := 0 135 | } 136 | } 137 | 138 | // Only if still successful 139 | // For <1 word tail bytes 140 | if gt(success, 0) { 141 | // Get the remainder of length/32 142 | // length % 32 = AND(length, 32 - 1) 143 | let numTailBytes := and(length, 0x1f) 144 | let mcRem := mload(mc) 145 | let ccRem := mload(cc) 146 | for { 147 | let i := 0 148 | // the next line is the loop condition: 149 | // while(uint256(i < numTailBytes) + cb == 2) 150 | } eq(add(lt(i, numTailBytes), cb), 2) { 151 | i := add(i, 1) 152 | } { 153 | if iszero(eq(byte(i, mcRem), byte(i, ccRem))) { 154 | // unsuccess: 155 | success := 0 156 | cb := 0 157 | } 158 | } 159 | } 160 | } 161 | default { 162 | // unsuccess: 163 | success := 0 164 | } 165 | } 166 | 167 | return success; 168 | } 169 | 170 | function equal_nonAligned(bytes memory _a, bytes memory _b, string memory message) internal returns (bool) { 171 | bool returnBool = _equal_nonAligned(_a, _b); 172 | 173 | _report(returnBool, message); 174 | 175 | return returnBool; 176 | } 177 | 178 | function notEqual_nonAligned(bytes memory _a, bytes memory _b, string memory message) internal returns (bool) { 179 | bool returnBool = _equal_nonAligned(_a, _b); 180 | 181 | _report(!returnBool, message); 182 | 183 | return !returnBool; 184 | } 185 | 186 | /* 187 | Function: equal(bytes storage, bytes memory) 188 | 189 | Assert that two tightly packed bytes arrays are equal. 190 | 191 | Params: 192 | A (bytes) - The first bytes. 193 | B (bytes) - The second bytes. 194 | message (string) - A message that is sent if the assertion fails. 195 | 196 | Returns: 197 | result (bool) - The result. 198 | */ 199 | function _equalStorage(bytes storage _a, bytes memory _b) internal view returns (bool) { 200 | bool returnBool = true; 201 | 202 | assembly { 203 | // we know _a_offset is 0 204 | let fslot := sload(_a.slot) 205 | let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) 206 | let mlength := mload(_b) 207 | 208 | // if lengths don't match the arrays are not equal 209 | switch eq(slength, mlength) 210 | case 1 { 211 | // slength can contain both the length and contents of the array 212 | // if length < 32 bytes so let's prepare for that 213 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage 214 | if iszero(iszero(slength)) { 215 | switch lt(slength, 32) 216 | case 1 { 217 | // blank the last byte which is the length 218 | fslot := mul(div(fslot, 0x100), 0x100) 219 | 220 | if iszero(eq(fslot, mload(add(_b, 0x20)))) { 221 | // unsuccess: 222 | returnBool := 0 223 | } 224 | } 225 | default { 226 | // cb is a circuit breaker in the for loop since there's 227 | // no said feature for inline assembly loops 228 | // cb = 1 - don't breaker 229 | // cb = 0 - break 230 | let cb := 1 231 | 232 | // get the keccak hash to get the contents of the array 233 | mstore(0x0, _a.slot) 234 | let sc := keccak256(0x0, 0x20) 235 | 236 | let mc := add(_b, 0x20) 237 | let end := add(mc, mlength) 238 | 239 | // the next line is the loop condition: 240 | // while(uint256(mc < end) + cb == 2) 241 | for {} eq(add(lt(mc, end), cb), 2) { 242 | sc := add(sc, 1) 243 | mc := add(mc, 0x20) 244 | } { 245 | if iszero(eq(sload(sc), mload(mc))) { 246 | // unsuccess: 247 | returnBool := 0 248 | cb := 0 249 | } 250 | } 251 | } 252 | } 253 | } 254 | default { 255 | // unsuccess: 256 | returnBool := 0 257 | } 258 | } 259 | 260 | return returnBool; 261 | } 262 | 263 | function equalStorage(bytes storage _a, bytes memory _b, string memory message) internal returns (bool) { 264 | bool returnBool = _equalStorage(_a, _b); 265 | 266 | _report(returnBool, message); 267 | 268 | return returnBool; 269 | } 270 | 271 | function notEqualStorage(bytes storage _a, bytes memory _b, string memory message) internal returns (bool) { 272 | bool returnBool = _equalStorage(_a, _b); 273 | 274 | _report(!returnBool, message); 275 | 276 | return !returnBool; 277 | } 278 | 279 | /********** Maintaining compatibility with Truffle's Assert.sol ***********/ 280 | /******************************** internal ********************************/ 281 | 282 | /* 283 | Function: _report 284 | 285 | Internal function for triggering . 286 | 287 | Params: 288 | result (bool) - The test result (true or false). 289 | message (string) - The message that is sent if the assertion fails. 290 | */ 291 | function _report(bool result, string memory message) internal { 292 | if (result) 293 | emit TestEvent(true, ""); 294 | else 295 | emit TestEvent(false, message); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /contracts/BytesLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | /* 3 | * @title Solidity Bytes Arrays Utils 4 | * @author Gonçalo Sá 5 | * 6 | * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. 7 | * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. 8 | */ 9 | pragma solidity >=0.8.0 <0.9.0; 10 | 11 | 12 | library BytesLib { 13 | function concat( 14 | bytes memory _preBytes, 15 | bytes memory _postBytes 16 | ) 17 | internal 18 | pure 19 | returns (bytes memory) 20 | { 21 | bytes memory tempBytes; 22 | 23 | assembly { 24 | // Get a location of some free memory and store it in tempBytes as 25 | // Solidity does for memory variables. 26 | tempBytes := mload(0x40) 27 | 28 | // Store the length of the first bytes array at the beginning of 29 | // the memory for tempBytes. 30 | let length := mload(_preBytes) 31 | mstore(tempBytes, length) 32 | 33 | // Maintain a memory counter for the current write location in the 34 | // temp bytes array by adding the 32 bytes for the array length to 35 | // the starting location. 36 | let mc := add(tempBytes, 0x20) 37 | // Stop copying when the memory counter reaches the length of the 38 | // first bytes array. 39 | let end := add(mc, length) 40 | 41 | for { 42 | // Initialize a copy counter to the start of the _preBytes data, 43 | // 32 bytes into its memory. 44 | let cc := add(_preBytes, 0x20) 45 | } lt(mc, end) { 46 | // Increase both counters by 32 bytes each iteration. 47 | mc := add(mc, 0x20) 48 | cc := add(cc, 0x20) 49 | } { 50 | // Write the _preBytes data into the tempBytes memory 32 bytes 51 | // at a time. 52 | mstore(mc, mload(cc)) 53 | } 54 | 55 | // Add the length of _postBytes to the current length of tempBytes 56 | // and store it as the new length in the first 32 bytes of the 57 | // tempBytes memory. 58 | length := mload(_postBytes) 59 | mstore(tempBytes, add(length, mload(tempBytes))) 60 | 61 | // Move the memory counter back from a multiple of 0x20 to the 62 | // actual end of the _preBytes data. 63 | mc := end 64 | // Stop copying when the memory counter reaches the new combined 65 | // length of the arrays. 66 | end := add(mc, length) 67 | 68 | for { 69 | let cc := add(_postBytes, 0x20) 70 | } lt(mc, end) { 71 | mc := add(mc, 0x20) 72 | cc := add(cc, 0x20) 73 | } { 74 | mstore(mc, mload(cc)) 75 | } 76 | 77 | // Update the free-memory pointer by padding our last write location 78 | // to 32 bytes: add 31 bytes to the end of tempBytes to move to the 79 | // next 32 byte block, then round down to the nearest multiple of 80 | // 32. If the sum of the length of the two arrays is zero then add 81 | // one before rounding down to leave a blank 32 bytes (the length block with 0). 82 | mstore(0x40, and( 83 | add(add(end, iszero(add(length, mload(_preBytes)))), 31), 84 | not(31) // Round down to the nearest 32 bytes. 85 | )) 86 | } 87 | 88 | return tempBytes; 89 | } 90 | 91 | function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { 92 | assembly { 93 | // Read the first 32 bytes of _preBytes storage, which is the length 94 | // of the array. (We don't need to use the offset into the slot 95 | // because arrays use the entire slot.) 96 | let fslot := sload(_preBytes.slot) 97 | // Arrays of 31 bytes or less have an even value in their slot, 98 | // while longer arrays have an odd value. The actual length is 99 | // the slot divided by two for odd values, and the lowest order 100 | // byte divided by two for even values. 101 | // If the slot is even, bitwise and the slot with 255 and divide by 102 | // two to get the length. If the slot is odd, bitwise and the slot 103 | // with -1 and divide by two. 104 | let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) 105 | let mlength := mload(_postBytes) 106 | let newlength := add(slength, mlength) 107 | // slength can contain both the length and contents of the array 108 | // if length < 32 bytes so let's prepare for that 109 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage 110 | switch add(lt(slength, 32), lt(newlength, 32)) 111 | case 2 { 112 | // Since the new array still fits in the slot, we just need to 113 | // update the contents of the slot. 114 | // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length 115 | sstore( 116 | _preBytes.slot, 117 | // all the modifications to the slot are inside this 118 | // next block 119 | add( 120 | // we can just add to the slot contents because the 121 | // bytes we want to change are the LSBs 122 | fslot, 123 | add( 124 | mul( 125 | div( 126 | // load the bytes from memory 127 | mload(add(_postBytes, 0x20)), 128 | // zero all bytes to the right 129 | exp(0x100, sub(32, mlength)) 130 | ), 131 | // and now shift left the number of bytes to 132 | // leave space for the length in the slot 133 | exp(0x100, sub(32, newlength)) 134 | ), 135 | // increase length by the double of the memory 136 | // bytes length 137 | mul(mlength, 2) 138 | ) 139 | ) 140 | ) 141 | } 142 | case 1 { 143 | // The stored value fits in the slot, but the combined value 144 | // will exceed it. 145 | // get the keccak hash to get the contents of the array 146 | mstore(0x0, _preBytes.slot) 147 | let sc := add(keccak256(0x0, 0x20), div(slength, 32)) 148 | 149 | // save new length 150 | sstore(_preBytes.slot, add(mul(newlength, 2), 1)) 151 | 152 | // The contents of the _postBytes array start 32 bytes into 153 | // the structure. Our first read should obtain the `submod` 154 | // bytes that can fit into the unused space in the last word 155 | // of the stored array. To get this, we read 32 bytes starting 156 | // from `submod`, so the data we read overlaps with the array 157 | // contents by `submod` bytes. Masking the lowest-order 158 | // `submod` bytes allows us to add that value directly to the 159 | // stored value. 160 | 161 | let submod := sub(32, slength) 162 | let mc := add(_postBytes, submod) 163 | let end := add(_postBytes, mlength) 164 | let mask := sub(exp(0x100, submod), 1) 165 | 166 | sstore( 167 | sc, 168 | add( 169 | and( 170 | fslot, 171 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 172 | ), 173 | and(mload(mc), mask) 174 | ) 175 | ) 176 | 177 | for { 178 | mc := add(mc, 0x20) 179 | sc := add(sc, 1) 180 | } lt(mc, end) { 181 | sc := add(sc, 1) 182 | mc := add(mc, 0x20) 183 | } { 184 | sstore(sc, mload(mc)) 185 | } 186 | 187 | mask := exp(0x100, sub(mc, end)) 188 | 189 | sstore(sc, mul(div(mload(mc), mask), mask)) 190 | } 191 | default { 192 | // get the keccak hash to get the contents of the array 193 | mstore(0x0, _preBytes.slot) 194 | // Start copying to the last used word of the stored array. 195 | let sc := add(keccak256(0x0, 0x20), div(slength, 32)) 196 | 197 | // save new length 198 | sstore(_preBytes.slot, add(mul(newlength, 2), 1)) 199 | 200 | // Copy over the first `submod` bytes of the new data as in 201 | // case 1 above. 202 | let slengthmod := mod(slength, 32) 203 | let mlengthmod := mod(mlength, 32) 204 | let submod := sub(32, slengthmod) 205 | let mc := add(_postBytes, submod) 206 | let end := add(_postBytes, mlength) 207 | let mask := sub(exp(0x100, submod), 1) 208 | 209 | sstore(sc, add(sload(sc), and(mload(mc), mask))) 210 | 211 | for { 212 | sc := add(sc, 1) 213 | mc := add(mc, 0x20) 214 | } lt(mc, end) { 215 | sc := add(sc, 1) 216 | mc := add(mc, 0x20) 217 | } { 218 | sstore(sc, mload(mc)) 219 | } 220 | 221 | mask := exp(0x100, sub(mc, end)) 222 | 223 | sstore(sc, mul(div(mload(mc), mask), mask)) 224 | } 225 | } 226 | } 227 | 228 | function slice( 229 | bytes memory _bytes, 230 | uint256 _start, 231 | uint256 _length 232 | ) 233 | internal 234 | pure 235 | returns (bytes memory) 236 | { 237 | // We're using the unchecked block below because otherwise execution ends 238 | // with the native overflow error code. 239 | unchecked { 240 | require(_length + 31 >= _length, "slice_overflow"); 241 | } 242 | require(_bytes.length >= _start + _length, "slice_outOfBounds"); 243 | 244 | bytes memory tempBytes; 245 | 246 | assembly { 247 | switch iszero(_length) 248 | case 0 { 249 | // Get a location of some free memory and store it in tempBytes as 250 | // Solidity does for memory variables. 251 | tempBytes := mload(0x40) 252 | 253 | // The first word of the slice result is potentially a partial 254 | // word read from the original array. To read it, we calculate 255 | // the length of that partial word and start copying that many 256 | // bytes into the array. The first word we copy will start with 257 | // data we don't care about, but the last `lengthmod` bytes will 258 | // land at the beginning of the contents of the new array. When 259 | // we're done copying, we overwrite the full first word with 260 | // the actual length of the slice. 261 | let lengthmod := and(_length, 31) 262 | 263 | // The multiplication in the next line is necessary 264 | // because when slicing multiples of 32 bytes (lengthmod == 0) 265 | // the following copy loop was copying the origin's length 266 | // and then ending prematurely not copying everything it should. 267 | let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) 268 | let end := add(mc, _length) 269 | 270 | for { 271 | // The multiplication in the next line has the same exact purpose 272 | // as the one above. 273 | let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) 274 | } lt(mc, end) { 275 | mc := add(mc, 0x20) 276 | cc := add(cc, 0x20) 277 | } { 278 | mstore(mc, mload(cc)) 279 | } 280 | 281 | mstore(tempBytes, _length) 282 | 283 | //update free-memory pointer 284 | //allocating the array padded to 32 bytes like the compiler does now 285 | mstore(0x40, and(add(mc, 31), not(31))) 286 | } 287 | //if we want a zero-length slice let's just return a zero-length array 288 | default { 289 | tempBytes := mload(0x40) 290 | //zero out the 32 bytes slice we are about to return 291 | //we need to do it because Solidity does not garbage collect 292 | mstore(tempBytes, 0) 293 | 294 | mstore(0x40, add(tempBytes, 0x20)) 295 | } 296 | } 297 | 298 | return tempBytes; 299 | } 300 | 301 | function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { 302 | require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); 303 | address tempAddress; 304 | 305 | assembly { 306 | tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) 307 | } 308 | 309 | return tempAddress; 310 | } 311 | 312 | function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { 313 | require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); 314 | uint8 tempUint; 315 | 316 | assembly { 317 | tempUint := mload(add(add(_bytes, 0x1), _start)) 318 | } 319 | 320 | return tempUint; 321 | } 322 | 323 | function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { 324 | require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); 325 | uint16 tempUint; 326 | 327 | assembly { 328 | tempUint := mload(add(add(_bytes, 0x2), _start)) 329 | } 330 | 331 | return tempUint; 332 | } 333 | 334 | function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { 335 | require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); 336 | uint32 tempUint; 337 | 338 | assembly { 339 | tempUint := mload(add(add(_bytes, 0x4), _start)) 340 | } 341 | 342 | return tempUint; 343 | } 344 | 345 | function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { 346 | require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); 347 | uint64 tempUint; 348 | 349 | assembly { 350 | tempUint := mload(add(add(_bytes, 0x8), _start)) 351 | } 352 | 353 | return tempUint; 354 | } 355 | 356 | function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { 357 | require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); 358 | uint96 tempUint; 359 | 360 | assembly { 361 | tempUint := mload(add(add(_bytes, 0xc), _start)) 362 | } 363 | 364 | return tempUint; 365 | } 366 | 367 | function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { 368 | require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); 369 | uint128 tempUint; 370 | 371 | assembly { 372 | tempUint := mload(add(add(_bytes, 0x10), _start)) 373 | } 374 | 375 | return tempUint; 376 | } 377 | 378 | function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { 379 | require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); 380 | uint256 tempUint; 381 | 382 | assembly { 383 | tempUint := mload(add(add(_bytes, 0x20), _start)) 384 | } 385 | 386 | return tempUint; 387 | } 388 | 389 | function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { 390 | require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); 391 | bytes32 tempBytes32; 392 | 393 | assembly { 394 | tempBytes32 := mload(add(add(_bytes, 0x20), _start)) 395 | } 396 | 397 | return tempBytes32; 398 | } 399 | 400 | function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { 401 | bool success = true; 402 | 403 | assembly { 404 | let length := mload(_preBytes) 405 | 406 | // if lengths don't match the arrays are not equal 407 | switch eq(length, mload(_postBytes)) 408 | case 1 { 409 | // cb is a circuit breaker in the for loop since there's 410 | // no said feature for inline assembly loops 411 | // cb = 1 - don't breaker 412 | // cb = 0 - break 413 | let cb := 1 414 | 415 | let mc := add(_preBytes, 0x20) 416 | let end := add(mc, length) 417 | 418 | for { 419 | let cc := add(_postBytes, 0x20) 420 | // the next line is the loop condition: 421 | // while(uint256(mc < end) + cb == 2) 422 | } eq(add(lt(mc, end), cb), 2) { 423 | mc := add(mc, 0x20) 424 | cc := add(cc, 0x20) 425 | } { 426 | // if any of these checks fails then arrays are not equal 427 | if iszero(eq(mload(mc), mload(cc))) { 428 | // unsuccess: 429 | success := 0 430 | cb := 0 431 | } 432 | } 433 | } 434 | default { 435 | // unsuccess: 436 | success := 0 437 | } 438 | } 439 | 440 | return success; 441 | } 442 | 443 | function equalStorage( 444 | bytes storage _preBytes, 445 | bytes memory _postBytes 446 | ) 447 | internal 448 | view 449 | returns (bool) 450 | { 451 | bool success = true; 452 | 453 | assembly { 454 | // we know _preBytes_offset is 0 455 | let fslot := sload(_preBytes.slot) 456 | // Decode the length of the stored array like in concatStorage(). 457 | let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) 458 | let mlength := mload(_postBytes) 459 | 460 | // if lengths don't match the arrays are not equal 461 | switch eq(slength, mlength) 462 | case 1 { 463 | // slength can contain both the length and contents of the array 464 | // if length < 32 bytes so let's prepare for that 465 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage 466 | if iszero(iszero(slength)) { 467 | switch lt(slength, 32) 468 | case 1 { 469 | // blank the last byte which is the length 470 | fslot := mul(div(fslot, 0x100), 0x100) 471 | 472 | if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { 473 | // unsuccess: 474 | success := 0 475 | } 476 | } 477 | default { 478 | // cb is a circuit breaker in the for loop since there's 479 | // no said feature for inline assembly loops 480 | // cb = 1 - don't breaker 481 | // cb = 0 - break 482 | let cb := 1 483 | 484 | // get the keccak hash to get the contents of the array 485 | mstore(0x0, _preBytes.slot) 486 | let sc := keccak256(0x0, 0x20) 487 | 488 | let mc := add(_postBytes, 0x20) 489 | let end := add(mc, mlength) 490 | 491 | // the next line is the loop condition: 492 | // while(uint256(mc < end) + cb == 2) 493 | for {} eq(add(lt(mc, end), cb), 2) { 494 | sc := add(sc, 1) 495 | mc := add(mc, 0x20) 496 | } { 497 | if iszero(eq(sload(sc), mload(mc))) { 498 | // unsuccess: 499 | success := 0 500 | cb := 0 501 | } 502 | } 503 | } 504 | } 505 | } 506 | default { 507 | // unsuccess: 508 | success := 0 509 | } 510 | } 511 | 512 | return success; 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /ethpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "package_name": "bytes", 3 | "version": "0.8.0", 4 | "description": "Solidity bytes tightly packed arrays utility library.", 5 | "authors": [ 6 | "Gonçalo Sá " 7 | ], 8 | "keywords": [ 9 | "bytes", 10 | "utils", 11 | "solidity", 12 | "library" 13 | ], 14 | "dependencies": { 15 | }, 16 | "license": "MIT" 17 | } -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc-version = '0.8.22' 3 | src = 'contracts' 4 | test = 'test' 5 | cache_path = 'cache' 6 | libs = ['lib'] 7 | -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0xd63cb23c26cc3bda03b1cc2971589fd26c9d22f3bbc8f1ff2bb74613e6d1c36d" 4 | } 5 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-bytes-utils", 3 | "version": "0.8.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "solidity-bytes-utils", 9 | "version": "0.8.4", 10 | "license": "MIT" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-bytes-utils", 3 | "version": "0.8.4", 4 | "description": "Solidity bytes tightly packed arrays utility library.", 5 | "main": "truffle.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:GNSPS/solidity-bytes-utils.git" 9 | }, 10 | "files": [ 11 | "contracts", 12 | "test" 13 | ], 14 | "keywords": [ 15 | "ethereum", 16 | "bytes", 17 | "solidity", 18 | "library" 19 | ], 20 | "authors": [ 21 | "Gonçalo Sá " 22 | ], 23 | "scripts": { 24 | "test": "forge test" 25 | }, 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/GNSPS/solidity-bytes-utils/issues" 29 | }, 30 | "homepage": "https://github.com/GNSPS/solidity-bytes-utils#readme" 31 | } 32 | -------------------------------------------------------------------------------- /test/TestBytesLib1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../contracts/AssertBytes.sol"; 6 | import "../contracts/BytesLib.sol"; 7 | 8 | 9 | contract TestBytesLib1 is Test { 10 | using BytesLib for bytes; 11 | 12 | bytes storageCheckBytes = hex"aabbccddeeff"; 13 | bytes storageCheckBytesZeroLength = hex""; 14 | 15 | bytes storageBytes4 = hex"f00dfeed"; 16 | bytes storageBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 17 | bytes storageBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 18 | bytes storageBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 19 | bytes storageBytes63 = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 20 | bytes storageBytes64 = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 21 | bytes storageBytes65 = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 22 | bytes storageBytes70 = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 23 | 24 | /** 25 | * Sanity Checks 26 | */ 27 | 28 | function testSanityCheck() public { 29 | // Assert library sanity checks 30 | // 31 | // Please don't change the ordering of the var definitions 32 | // the order is purposeful for testing zero-length arrays 33 | bytes memory checkBytes = hex"aabbccddeeff"; 34 | bytes memory checkBytesZeroLength = hex""; 35 | 36 | bytes memory checkBytesRight = hex"aabbccddeeff"; 37 | bytes memory checkBytesZeroLengthRight = hex""; 38 | bytes memory checkBytesWrongLength = hex"aa0000"; 39 | bytes memory checkBytesWrongContent = hex"aabbccddee00"; 40 | 41 | assertEq(checkBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out."); 42 | assertTrue(keccak256(checkBytes) != keccak256(checkBytesWrongLength), "Sanity check should be checking different length bytes arrays out."); 43 | assertTrue(keccak256(checkBytes) != keccak256(checkBytesWrongContent), "Sanity check should be checking different content bytes arrays out."); 44 | AssertBytes.equal(checkBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out."); 45 | AssertBytes.notEqual(checkBytes, checkBytesWrongLength, "Sanity check should be checking different length bytes arrays out."); 46 | AssertBytes.notEqual(checkBytes, checkBytesWrongContent, "Sanity check should be checking different content bytes arrays out."); 47 | 48 | assertEq(storageCheckBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out. (Storage)"); 49 | assertTrue(keccak256(storageCheckBytes) != keccak256(checkBytesWrongLength), "Sanity check should be checking different length bytes arrays out. (Storage)"); 50 | assertTrue(keccak256(storageCheckBytes) != keccak256(checkBytesWrongContent), "Sanity check should be checking different content bytes arrays out. (Storage)"); 51 | AssertBytes.equalStorage(storageCheckBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out. (Storage)"); 52 | AssertBytes.notEqualStorage(storageCheckBytes, checkBytesWrongLength, "Sanity check should be checking different length bytes arrays out. (Storage)"); 53 | AssertBytes.notEqualStorage(storageCheckBytes, checkBytesWrongContent, "Sanity check should be checking different content bytes arrays out. (Storage)"); 54 | 55 | // Zero-length checks 56 | assertEq(checkBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out."); 57 | assertTrue(keccak256(checkBytesZeroLength) != keccak256(checkBytes), "Sanity check should be checking different length bytes arrays out."); 58 | AssertBytes.equal(checkBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out."); 59 | AssertBytes.notEqual(checkBytesZeroLength, checkBytes, "Sanity check should be checking different length bytes arrays out."); 60 | 61 | assertEq(storageCheckBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out. (Storage)"); 62 | assertTrue(keccak256(storageCheckBytesZeroLength) != keccak256(checkBytes), "Sanity check should be checking different length bytes arrays out. (Storage)"); 63 | AssertBytes.equalStorage(storageCheckBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out. (Storage)"); 64 | AssertBytes.notEqualStorage(storageCheckBytesZeroLength, checkBytes, "Sanity check should be checking different length bytes arrays out. (Storage)"); 65 | } 66 | 67 | /** 68 | * Memory Integrity Checks 69 | */ 70 | 71 | function testMemoryIntegrityCheck4Bytes() public { 72 | bytes memory preBytes4 = hex"f00dfeed"; 73 | bytes memory postBytes4 = hex"f00dfeed"; 74 | bytes memory postBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 75 | bytes memory postBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 76 | bytes memory postBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 77 | 78 | bytes memory testBytes; 79 | bytes memory resultBytes; 80 | 81 | resultBytes = preBytes4.concat(postBytes4); 82 | 83 | // Now we should make sure that all the other previously initialized arrays stayed the same 84 | testBytes = hex"f00dfeed"; 85 | assertEq(preBytes4, testBytes, "After a postBytes4 concat the preBytes4 integrity check failed."); 86 | assertEq(postBytes4, testBytes, "After a postBytes4 concat the postBytes4 integrity check failed."); 87 | AssertBytes.equal(preBytes4, testBytes, "After a postBytes4 concat the preBytes4 integrity check failed."); 88 | AssertBytes.equal(postBytes4, testBytes, "After a postBytes4 concat the postBytes4 integrity check failed."); 89 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 90 | assertEq(postBytes31, testBytes, "After a postBytes4 concat the postBytes31 integrity check failed."); 91 | AssertBytes.equal(postBytes31, testBytes, "After a postBytes4 concat the postBytes31 integrity check failed."); 92 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 93 | assertEq(postBytes32, testBytes, "After a postBytes4 concat the postBytes32 integrity check failed."); 94 | AssertBytes.equal(postBytes32, testBytes, "After a postBytes4 concat the postBytes32 integrity check failed."); 95 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 96 | assertEq(postBytes33, testBytes, "After a postBytes4 concat the postBytes33 integrity check failed."); 97 | AssertBytes.equal(postBytes33, testBytes, "After a postBytes4 concat the postBytes33 integrity check failed."); 98 | } 99 | 100 | function testMemoryIntegrityCheck31Bytes() public { 101 | bytes memory preBytes4 = hex"f00dfeed"; 102 | bytes memory postBytes4 = hex"f00dfeed"; 103 | bytes memory postBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 104 | bytes memory postBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 105 | bytes memory postBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 106 | 107 | bytes memory testBytes; 108 | bytes memory resultBytes; 109 | 110 | resultBytes = preBytes4.concat(postBytes31); 111 | 112 | // Now we should make sure that all the other previously initialized arrays stayed the same 113 | testBytes = hex"f00dfeed"; 114 | assertEq(preBytes4, testBytes, "After a postBytes31 concat the preBytes4 integrity check failed."); 115 | assertEq(postBytes4, testBytes, "After a postBytes31 concat the postBytes4 integrity check failed."); 116 | AssertBytes.equal(preBytes4, testBytes, "After a postBytes31 concat the preBytes4 integrity check failed."); 117 | AssertBytes.equal(postBytes4, testBytes, "After a postBytes31 concat the postBytes4 integrity check failed."); 118 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 119 | assertEq(postBytes31, testBytes, "After a postBytes31 concat the postBytes31 integrity check failed."); 120 | AssertBytes.equal(postBytes31, testBytes, "After a postBytes31 concat the postBytes31 integrity check failed."); 121 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 122 | assertEq(postBytes32, testBytes, "After a postBytes31 concat the postBytes32 integrity check failed."); 123 | AssertBytes.equal(postBytes32, testBytes, "After a postBytes31 concat the postBytes32 integrity check failed."); 124 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 125 | assertEq(postBytes33, testBytes, "After a postBytes31 concat the postBytes33 integrity check failed."); 126 | AssertBytes.equal(postBytes33, testBytes, "After a postBytes31 concat the postBytes33 integrity check failed."); 127 | } 128 | 129 | function testMemoryIntegrityCheck32Bytes() public { 130 | bytes memory preBytes4 = hex"f00dfeed"; 131 | bytes memory postBytes4 = hex"f00dfeed"; 132 | bytes memory postBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 133 | bytes memory postBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 134 | bytes memory postBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 135 | 136 | bytes memory testBytes; 137 | bytes memory resultBytes; 138 | 139 | resultBytes = preBytes4.concat(postBytes32); 140 | 141 | // Now we should make sure that all the other previously initialized arrays stayed the same 142 | testBytes = hex"f00dfeed"; 143 | assertEq(preBytes4, testBytes, "After a postBytes32 concat the preBytes4 integrity check failed."); 144 | assertEq(postBytes4, testBytes, "After a postBytes32 concat the postBytes4 integrity check failed."); 145 | AssertBytes.equal(preBytes4, testBytes, "After a postBytes32 concat the preBytes4 integrity check failed."); 146 | AssertBytes.equal(postBytes4, testBytes, "After a postBytes32 concat the postBytes4 integrity check failed."); 147 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 148 | assertEq(postBytes31, testBytes, "After a postBytes32 concat the postBytes31 integrity check failed."); 149 | AssertBytes.equal(postBytes31, testBytes, "After a postBytes32 concat the postBytes31 integrity check failed."); 150 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 151 | assertEq(postBytes32, testBytes, "After a postBytes32 concat the postBytes32 integrity check failed."); 152 | AssertBytes.equal(postBytes32, testBytes, "After a postBytes32 concat the postBytes32 integrity check failed."); 153 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 154 | assertEq(postBytes33, testBytes, "After a postBytes32 concat the postBytes33 integrity check failed."); 155 | AssertBytes.equal(postBytes33, testBytes, "After a postBytes32 concat the postBytes33 integrity check failed."); 156 | } 157 | 158 | function testMemoryIntegrityCheck33Bytes() public { 159 | bytes memory preBytes4 = hex"f00dfeed"; 160 | bytes memory postBytes4 = hex"f00dfeed"; 161 | bytes memory postBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 162 | bytes memory postBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 163 | bytes memory postBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 164 | 165 | bytes memory testBytes; 166 | bytes memory resultBytes; 167 | 168 | resultBytes = preBytes4.concat(postBytes33); 169 | 170 | // Now we should make sure that all the other previously initialized arrays stayed the same 171 | testBytes = hex"f00dfeed"; 172 | assertEq(preBytes4, testBytes, "After a postBytes33 concat the preBytes4 integrity check failed."); 173 | assertEq(postBytes4, testBytes, "After a postBytes33 concat the postBytes4 integrity check failed."); 174 | AssertBytes.equal(preBytes4, testBytes, "After a postBytes33 concat the preBytes4 integrity check failed."); 175 | AssertBytes.equal(postBytes4, testBytes, "After a postBytes33 concat the postBytes4 integrity check failed."); 176 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 177 | assertEq(postBytes31, testBytes, "After a postBytes33 concat the postBytes31 integrity check failed."); 178 | AssertBytes.equal(postBytes31, testBytes, "After a postBytes33 concat the postBytes31 integrity check failed."); 179 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 180 | assertEq(postBytes32, testBytes, "After a postBytes33 concat the postBytes32 integrity check failed."); 181 | AssertBytes.equal(postBytes32, testBytes, "After a postBytes33 concat the postBytes32 integrity check failed."); 182 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 183 | assertEq(postBytes33, testBytes, "After a postBytes33 concat the postBytes33 integrity check failed."); 184 | AssertBytes.equal(postBytes33, testBytes, "After a postBytes33 concat the postBytes33 integrity check failed."); 185 | } 186 | 187 | /** 188 | * Memory Concatenation Tests 189 | */ 190 | 191 | function testConcatMemory4Bytes() public { 192 | // Initialize `bytes` variables in memory with different critical sizes 193 | bytes memory preBytes4 = hex"f00dfeed"; 194 | bytes memory preBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 195 | bytes memory preBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 196 | bytes memory preBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 197 | 198 | bytes memory postBytes4 = hex"f00dfeed"; 199 | 200 | bytes memory testBytes; 201 | bytes memory resultBytes; 202 | 203 | resultBytes = preBytes4.concat(postBytes4); 204 | testBytes = hex"f00dfeedf00dfeed"; 205 | assertEq(resultBytes, testBytes, "preBytes4 + postBytes4 concatenation failed."); 206 | AssertBytes.equal(resultBytes, testBytes, "preBytes4 + postBytes4 concatenation failed."); 207 | 208 | resultBytes = preBytes31.concat(postBytes4); 209 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00dfeed"; 210 | assertEq(resultBytes, testBytes, "preBytes31 + postBytes4 concatenation failed."); 211 | AssertBytes.equal(resultBytes, testBytes, "preBytes31 + postBytes4 concatenation failed."); 212 | 213 | resultBytes = preBytes32.concat(postBytes4); 214 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00dfeed"; 215 | assertEq(resultBytes, testBytes, "preBytes32 + postBytes4 concatenation failed."); 216 | AssertBytes.equal(resultBytes, testBytes, "preBytes32 + postBytes4 concatenation failed."); 217 | 218 | resultBytes = preBytes33.concat(postBytes4); 219 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 220 | assertEq(resultBytes, testBytes, "preBytes33 + postBytes4 concatenation failed."); 221 | AssertBytes.equal(resultBytes, testBytes, "preBytes33 + postBytes4 concatenation failed."); 222 | } 223 | 224 | function testConcatMemory31Bytes() public { 225 | // Initialize `bytes` variables in memory with different critical sizes 226 | bytes memory preBytes4 = hex"f00dfeed"; 227 | bytes memory preBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 228 | bytes memory preBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 229 | bytes memory preBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 230 | 231 | bytes memory postBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 232 | 233 | bytes memory testBytes; 234 | bytes memory resultBytes; 235 | 236 | resultBytes = preBytes4.concat(postBytes31); 237 | testBytes = hex"f00dfeedf00d000000000000000000000000000000000000000000000000000000feed"; 238 | assertEq(resultBytes, testBytes, "preBytes4 + postBytes31 concatenation failed."); 239 | AssertBytes.equal(resultBytes, testBytes, "preBytes4 + postBytes31 concatenation failed."); 240 | 241 | resultBytes = preBytes31.concat(postBytes31); 242 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 243 | assertEq(resultBytes, testBytes, "preBytes31 + postBytes31 concatenation failed."); 244 | AssertBytes.equal(resultBytes, testBytes, "preBytes31 + postBytes31 concatenation failed."); 245 | 246 | resultBytes = preBytes32.concat(postBytes31); 247 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 248 | assertEq(resultBytes, testBytes, "preBytes32 + postBytes31 concatenation failed."); 249 | AssertBytes.equal(resultBytes, testBytes, "preBytes32 + postBytes31 concatenation failed."); 250 | 251 | resultBytes = preBytes33.concat(postBytes31); 252 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 253 | assertEq(resultBytes, testBytes, "preBytes33 + postBytes31 concatenation failed."); 254 | AssertBytes.equal(resultBytes, testBytes, "preBytes33 + postBytes31 concatenation failed."); 255 | } 256 | 257 | function testConcatMemory32Bytes() public { 258 | // Initialize `bytes` variables in memory with different critical sizes 259 | bytes memory preBytes4 = hex"f00dfeed"; 260 | bytes memory preBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 261 | bytes memory preBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 262 | bytes memory preBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 263 | 264 | bytes memory postBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 265 | 266 | bytes memory testBytes; 267 | bytes memory resultBytes; 268 | 269 | resultBytes = preBytes4.concat(postBytes32); 270 | testBytes = hex"f00dfeedf00d00000000000000000000000000000000000000000000000000000000feed"; 271 | assertEq(resultBytes, testBytes, "preBytes4 + postBytes32 concatenation failed."); 272 | AssertBytes.equal(resultBytes, testBytes, "preBytes4 + postBytes32 concatenation failed."); 273 | 274 | resultBytes = preBytes31.concat(postBytes32); 275 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 276 | assertEq(resultBytes, testBytes, "preBytes31 + postBytes32 concatenation failed."); 277 | AssertBytes.equal(resultBytes, testBytes, "preBytes31 + postBytes32 concatenation failed."); 278 | 279 | resultBytes = preBytes32.concat(postBytes32); 280 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 281 | assertEq(resultBytes, testBytes, "preBytes32 + postBytes32 concatenation failed."); 282 | AssertBytes.equal(resultBytes, testBytes, "preBytes32 + postBytes32 concatenation failed."); 283 | 284 | resultBytes = preBytes33.concat(postBytes32); 285 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 286 | assertEq(resultBytes, testBytes, "preBytes33 + postBytes32 concatenation failed."); 287 | AssertBytes.equal(resultBytes, testBytes, "preBytes33 + postBytes32 concatenation failed."); 288 | } 289 | 290 | function testConcatMemory33Bytes() public { 291 | // Initialize `bytes` variables in memory with different critical sizes 292 | bytes memory preBytes4 = hex"f00dfeed"; 293 | bytes memory preBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 294 | bytes memory preBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 295 | bytes memory preBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 296 | 297 | bytes memory postBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 298 | 299 | bytes memory testBytes; 300 | bytes memory resultBytes; 301 | 302 | resultBytes = preBytes4.concat(postBytes33); 303 | testBytes = hex"f00dfeedf00d0000000000000000000000000000000000000000000000000000000000feed"; 304 | assertEq(resultBytes, testBytes, "preBytes4 + postBytes33 concatenation failed."); 305 | AssertBytes.equal(resultBytes, testBytes, "preBytes4 + postBytes33 concatenation failed."); 306 | 307 | resultBytes = preBytes31.concat(postBytes33); 308 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 309 | assertEq(resultBytes, testBytes, "preBytes31 + postBytes33 concatenation failed."); 310 | AssertBytes.equal(resultBytes, testBytes, "preBytes31 + postBytes33 concatenation failed."); 311 | 312 | resultBytes = preBytes32.concat(postBytes33); 313 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 314 | assertEq(resultBytes, testBytes, "preBytes32 + postBytes33 concatenation failed."); 315 | AssertBytes.equal(resultBytes, testBytes, "preBytes32 + postBytes33 concatenation failed."); 316 | 317 | resultBytes = preBytes33.concat(postBytes33); 318 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 319 | assertEq(resultBytes, testBytes, "preBytes33 + postBytes33 concatenation failed."); 320 | AssertBytes.equal(resultBytes, testBytes, "preBytes33 + postBytes33 concatenation failed."); 321 | } 322 | 323 | /** 324 | * Storage Concatenation Tests 325 | */ 326 | 327 | function testConcatStorage4Bytes() public { 328 | // Initialize `bytes` variables in memory 329 | bytes memory memBytes4 = hex"f00dfeed"; 330 | 331 | // this next assembly block is to guarantee that the block of memory next to the end of the bytes 332 | // array is not zero'ed out 333 | assembly { 334 | let mc := mload(0x40) 335 | mstore(mc, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 336 | mstore(0x40, add(mc, 0x20)) 337 | } 338 | 339 | bytes memory testBytes; 340 | 341 | storageBytes4.concatStorage(memBytes4); 342 | testBytes = hex"f00dfeedf00dfeed"; 343 | assertEq(storageBytes4, testBytes, "storageBytes4 + memBytes4 concatenation failed."); 344 | AssertBytes.equalStorage(storageBytes4, testBytes, "storageBytes4 + memBytes4 concatenation failed."); 345 | 346 | storageBytes31.concatStorage(memBytes4); 347 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00dfeed"; 348 | assertEq(storageBytes31, testBytes, "storageBytes31 + memBytes4 concatenation failed."); 349 | AssertBytes.equalStorage(storageBytes31, testBytes, "storageBytes31 + memBytes4 concatenation failed."); 350 | 351 | storageBytes32.concatStorage(memBytes4); 352 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00dfeed"; 353 | assertEq(storageBytes32, testBytes, "storageBytes32 + memBytes4 concatenation failed."); 354 | AssertBytes.equalStorage(storageBytes32, testBytes, "storageBytes32 + memBytes4 concatenation failed."); 355 | 356 | storageBytes33.concatStorage(memBytes4); 357 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 358 | assertEq(storageBytes33, testBytes, "storageBytes33 + memBytes4 concatenation failed."); 359 | AssertBytes.equalStorage(storageBytes33, testBytes, "storageBytes33 + memBytes4 concatenation failed."); 360 | 361 | storageBytes63.concatStorage(memBytes4); 362 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 363 | assertEq(storageBytes63, testBytes, "storageBytes63 + memBytes4 concatenation failed."); 364 | AssertBytes.equalStorage(storageBytes63, testBytes, "storageBytes63 + memBytes4 concatenation failed."); 365 | 366 | storageBytes64.concatStorage(memBytes4); 367 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 368 | assertEq(storageBytes64, testBytes, "storageBytes64 + memBytes4 concatenation failed."); 369 | AssertBytes.equalStorage(storageBytes64, testBytes, "storageBytes64 + memBytes4 concatenation failed."); 370 | 371 | storageBytes65.concatStorage(memBytes4); 372 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 373 | assertEq(storageBytes65, testBytes, "storageBytes65 + memBytes4 concatenation failed."); 374 | AssertBytes.equalStorage(storageBytes65, testBytes, "storageBytes65 + memBytes4 concatenation failed."); 375 | 376 | storageBytes70.concatStorage(memBytes4); 377 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00dfeed"; 378 | assertEq(storageBytes70, testBytes, "storageBytes70 + memBytes4 concatenation failed."); 379 | AssertBytes.equalStorage(storageBytes70, testBytes, "storageBytes70 + memBytes4 concatenation failed."); 380 | 381 | resetStorage(); 382 | } 383 | 384 | function testConcatStorage31Bytes() public { 385 | // Initialize `bytes` variables in memory 386 | bytes memory memBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 387 | 388 | // this next assembly block is to guarantee that the block of memory next to the end of the bytes 389 | // array is not zero'ed out 390 | assembly { 391 | let mc := mload(0x40) 392 | mstore(mc, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 393 | mstore(0x40, add(mc, 0x20)) 394 | } 395 | 396 | bytes memory testBytes; 397 | 398 | storageBytes4.concatStorage(memBytes31); 399 | testBytes = hex"f00dfeedf00d000000000000000000000000000000000000000000000000000000feed"; 400 | assertEq(storageBytes4, testBytes, "storageBytes4 + memBytes31 concatenation failed."); 401 | AssertBytes.equalStorage(storageBytes4, testBytes, "storageBytes4 + memBytes31 concatenation failed."); 402 | 403 | storageBytes31.concatStorage(memBytes31); 404 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 405 | assertEq(storageBytes31, testBytes, "storageBytes31 + memBytes31 concatenation failed."); 406 | AssertBytes.equalStorage(storageBytes31, testBytes, "storageBytes31 + memBytes31 concatenation failed."); 407 | 408 | storageBytes32.concatStorage(memBytes31); 409 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 410 | assertEq(storageBytes32, testBytes, "storageBytes32 + memBytes31 concatenation failed."); 411 | AssertBytes.equalStorage(storageBytes32, testBytes, "storageBytes32 + memBytes31 concatenation failed."); 412 | 413 | storageBytes33.concatStorage(memBytes31); 414 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 415 | assertEq(storageBytes33, testBytes, "storageBytes33 + memBytes31 concatenation failed."); 416 | AssertBytes.equalStorage(storageBytes33, testBytes, "storageBytes33 + memBytes31 concatenation failed."); 417 | 418 | storageBytes63.concatStorage(memBytes31); 419 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 420 | assertEq(storageBytes63, testBytes, "storageBytes63 + memBytes31 concatenation failed."); 421 | AssertBytes.equalStorage(storageBytes63, testBytes, "storageBytes63 + memBytes31 concatenation failed."); 422 | 423 | storageBytes64.concatStorage(memBytes31); 424 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 425 | assertEq(storageBytes64, testBytes, "storageBytes64 + memBytes31 concatenation failed."); 426 | AssertBytes.equalStorage(storageBytes64, testBytes, "storageBytes64 + memBytes31 concatenation failed."); 427 | 428 | storageBytes65.concatStorage(memBytes31); 429 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 430 | assertEq(storageBytes65, testBytes, "storageBytes65 + memBytes31 concatenation failed."); 431 | AssertBytes.equalStorage(storageBytes65, testBytes, "storageBytes65 + memBytes31 concatenation failed."); 432 | 433 | storageBytes70.concatStorage(memBytes31); 434 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d000000000000000000000000000000000000000000000000000000feed"; 435 | assertEq(storageBytes70, testBytes, "storageBytes70 + memBytes31 concatenation failed."); 436 | AssertBytes.equalStorage(storageBytes70, testBytes, "storageBytes70 + memBytes31 concatenation failed."); 437 | 438 | resetStorage(); 439 | } 440 | 441 | function testConcatStorage32Bytes() public { 442 | // Initialize `bytes` variables in memory 443 | bytes memory memBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 444 | 445 | // this next assembly block is to guarantee that the block of memory next to the end of the bytes 446 | // array is not zero'ed out 447 | assembly { 448 | let mc := mload(0x40) 449 | mstore(mc, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 450 | mstore(0x40, add(mc, 0x20)) 451 | } 452 | 453 | bytes memory testBytes; 454 | 455 | storageBytes4.concatStorage(memBytes32); 456 | testBytes = hex"f00dfeedf00d00000000000000000000000000000000000000000000000000000000feed"; 457 | assertEq(storageBytes4, testBytes, "storageBytes4 + memBytes32 concatenation failed."); 458 | AssertBytes.equalStorage(storageBytes4, testBytes, "storageBytes4 + memBytes32 concatenation failed."); 459 | 460 | storageBytes31.concatStorage(memBytes32); 461 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 462 | assertEq(storageBytes31, testBytes, "storageBytes31 + memBytes32 concatenation failed."); 463 | AssertBytes.equalStorage(storageBytes31, testBytes, "storageBytes31 + memBytes32 concatenation failed."); 464 | 465 | storageBytes32.concatStorage(memBytes32); 466 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 467 | assertEq(storageBytes32, testBytes, "storageBytes32 + memBytes32 concatenation failed."); 468 | AssertBytes.equalStorage(storageBytes32, testBytes, "storageBytes32 + memBytes32 concatenation failed."); 469 | 470 | storageBytes33.concatStorage(memBytes32); 471 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 472 | assertEq(storageBytes33, testBytes, "storageBytes33 + memBytes32 concatenation failed."); 473 | AssertBytes.equalStorage(storageBytes33, testBytes, "storageBytes33 + memBytes32 concatenation failed."); 474 | 475 | storageBytes63.concatStorage(memBytes32); 476 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 477 | assertEq(storageBytes63, testBytes, "storageBytes63 + memBytes32 concatenation failed."); 478 | AssertBytes.equalStorage(storageBytes63, testBytes, "storageBytes63 + memBytes32 concatenation failed."); 479 | 480 | storageBytes64.concatStorage(memBytes32); 481 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 482 | assertEq(storageBytes64, testBytes, "storageBytes64 + memBytes32 concatenation failed."); 483 | AssertBytes.equalStorage(storageBytes64, testBytes, "storageBytes64 + memBytes32 concatenation failed."); 484 | 485 | storageBytes65.concatStorage(memBytes32); 486 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 487 | assertEq(storageBytes65, testBytes, "storageBytes65 + memBytes32 concatenation failed."); 488 | AssertBytes.equalStorage(storageBytes65, testBytes, "storageBytes65 + memBytes32 concatenation failed."); 489 | 490 | storageBytes70.concatStorage(memBytes32); 491 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 492 | assertEq(storageBytes70, testBytes, "storageBytes70 + memBytes32 concatenation failed."); 493 | AssertBytes.equalStorage(storageBytes70, testBytes, "storageBytes70 + memBytes32 concatenation failed."); 494 | 495 | resetStorage(); 496 | } 497 | 498 | function testConcatStorage33Bytes() public { 499 | // Initialize `bytes` variables in memory 500 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 501 | 502 | // this next assembly block is to guarantee that the block of memory next to the end of the bytes 503 | // array is not zero'ed out 504 | assembly { 505 | let mc := mload(0x40) 506 | mstore(mc, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 507 | mstore(0x40, add(mc, 0x20)) 508 | } 509 | 510 | bytes memory testBytes; 511 | 512 | storageBytes4.concatStorage(memBytes33); 513 | testBytes = hex"f00dfeedf00d0000000000000000000000000000000000000000000000000000000000feed"; 514 | assertEq(storageBytes4, testBytes, "storageBytes4 + memBytes33 concatenation failed."); 515 | AssertBytes.equalStorage(storageBytes4, testBytes, "storageBytes4 + memBytes33 concatenation failed."); 516 | 517 | storageBytes31.concatStorage(memBytes33); 518 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 519 | assertEq(storageBytes31, testBytes, "storageBytes31 + memBytes33 concatenation failed."); 520 | AssertBytes.equalStorage(storageBytes31, testBytes, "storageBytes31 + memBytes33 concatenation failed."); 521 | 522 | storageBytes32.concatStorage(memBytes33); 523 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 524 | assertEq(storageBytes32, testBytes, "storageBytes32 + memBytes33 concatenation failed."); 525 | AssertBytes.equalStorage(storageBytes32, testBytes, "storageBytes32 + memBytes33 concatenation failed."); 526 | 527 | storageBytes33.concatStorage(memBytes33); 528 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 529 | assertEq(storageBytes33, testBytes, "storageBytes33 + memBytes33 concatenation failed."); 530 | AssertBytes.equalStorage(storageBytes33, testBytes, "storageBytes33 + memBytes33 concatenation failed."); 531 | 532 | storageBytes63.concatStorage(memBytes33); 533 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 534 | assertEq(storageBytes63, testBytes, "storageBytes63 + memBytes33 concatenation failed."); 535 | AssertBytes.equalStorage(storageBytes63, testBytes, "storageBytes63 + memBytes33 concatenation failed."); 536 | 537 | storageBytes64.concatStorage(memBytes33); 538 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 539 | assertEq(storageBytes64, testBytes, "storageBytes64 + memBytes33 concatenation failed."); 540 | AssertBytes.equalStorage(storageBytes64, testBytes, "storageBytes64 + memBytes33 concatenation failed."); 541 | 542 | storageBytes65.concatStorage(memBytes33); 543 | testBytes = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 544 | assertEq(storageBytes65, testBytes, "storageBytes65 + memBytes33 concatenation failed."); 545 | AssertBytes.equalStorage(storageBytes65, testBytes, "storageBytes65 + memBytes33 concatenation failed."); 546 | 547 | storageBytes70.concatStorage(memBytes33); 548 | testBytes = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feedf00d0000000000000000000000000000000000000000000000000000000000feed"; 549 | assertEq(storageBytes70, testBytes, "storageBytes70 + memBytes33 concatenation failed."); 550 | AssertBytes.equalStorage(storageBytes70, testBytes, "storageBytes70 + memBytes33 concatenation failed."); 551 | 552 | resetStorage(); 553 | } 554 | 555 | /** 556 | * Equality Non-aligned Tests 557 | */ 558 | 559 | function testEqualNonAligned4Bytes() public { 560 | bytes memory memBytes1; // hex"f00dfeed" 561 | bytes memory memBytes2; // hex"f00dfeed" 562 | 563 | // We need to make sure that the bytes are not aligned to a 32 byte boundary 564 | // so we need to use assembly to allocate the bytes in contiguous memory 565 | // Solidity will not let us do this normally, this equality method exists 566 | // to test the edge case of non-aligned bytes created in assembly 567 | assembly { 568 | // Fetch free memory pointer 569 | let freePointer := mload(0x40) 570 | 571 | // We first store the length of the byte array (4 bytes) 572 | // And then we write a byte at a time 573 | memBytes1 := freePointer 574 | mstore(freePointer, 0x04) 575 | freePointer := add(freePointer, 0x20) 576 | mstore8(freePointer, 0xf0) 577 | freePointer := add(freePointer, 0x1) 578 | mstore8(freePointer, 0x0d) 579 | freePointer := add(freePointer, 0x1) 580 | mstore8(freePointer, 0xfe) 581 | freePointer := add(freePointer, 0x1) 582 | mstore8(freePointer, 0xed) 583 | freePointer := add(freePointer, 0x1) 584 | 585 | // We do the same for memBytes2 in contiguous memory 586 | memBytes2 := freePointer 587 | mstore(freePointer, 0x04) 588 | freePointer := add(freePointer, 0x20) 589 | mstore8(freePointer, 0xf0) 590 | freePointer := add(freePointer, 0x1) 591 | mstore8(freePointer, 0x0d) 592 | freePointer := add(freePointer, 0x1) 593 | mstore8(freePointer, 0xfe) 594 | freePointer := add(freePointer, 0x1) 595 | mstore8(freePointer, 0xed) 596 | freePointer := add(freePointer, 0x1) 597 | 598 | // We add some garbage bytes in contiguous memory 599 | mstore8(freePointer, 0xde) 600 | freePointer := add(freePointer, 0x1) 601 | mstore8(freePointer, 0xad) 602 | freePointer := add(freePointer, 0x1) 603 | 604 | // now, just for completeness sake we'll update the free-memory pointer accordingly 605 | mstore(0x40, freePointer) 606 | } 607 | 608 | AssertBytes.equal_nonAligned(memBytes1, memBytes2, "The equality check for the non-aligned equality 4-bytes-long test failed."); 609 | // The equality check for aligned byte arrays should fail for non-aligned bytes 610 | AssertBytes.notEqual(memBytes1, memBytes2, "The equality check for the non-aligned equality 4-bytes-long test failed."); 611 | } 612 | 613 | function testEqualNonAligned4BytesFail() public { 614 | bytes memory memBytes1; // hex"f00dfeed" 615 | bytes memory memBytes2; // hex"feedf00d" 616 | 617 | // We need to make sure that the bytes are not aligned to a 32 byte boundary 618 | // so we need to use assembly to allocate the bytes in contiguous memory 619 | // Solidity will not let us do this normally, this equality method exists 620 | // to test the edge case of non-aligned bytes created in assembly 621 | assembly { 622 | // Fetch free memory pointer 623 | let freePointer := mload(0x40) 624 | 625 | // We first store the length of the byte array (4 bytes) 626 | // And then we write a byte at a time 627 | memBytes1 := freePointer 628 | mstore(freePointer, 0x04) 629 | freePointer := add(freePointer, 0x20) 630 | mstore8(freePointer, 0xf0) 631 | freePointer := add(freePointer, 0x1) 632 | mstore8(freePointer, 0x0d) 633 | freePointer := add(freePointer, 0x1) 634 | mstore8(freePointer, 0xfe) 635 | freePointer := add(freePointer, 0x1) 636 | mstore8(freePointer, 0xed) 637 | freePointer := add(freePointer, 0x1) 638 | 639 | // We do the same for memBytes2 in contiguous memory 640 | memBytes2 := freePointer 641 | mstore(freePointer, 0x04) 642 | freePointer := add(freePointer, 0x20) 643 | mstore8(freePointer, 0xfe) 644 | freePointer := add(freePointer, 0x1) 645 | mstore8(freePointer, 0xed) 646 | freePointer := add(freePointer, 0x1) 647 | mstore8(freePointer, 0xf0) 648 | freePointer := add(freePointer, 0x1) 649 | mstore8(freePointer, 0x0d) 650 | freePointer := add(freePointer, 0x1) 651 | 652 | // We add some garbage bytes in contiguous memory 653 | mstore8(freePointer, 0xde) 654 | freePointer := add(freePointer, 0x1) 655 | mstore8(freePointer, 0xad) 656 | freePointer := add(freePointer, 0x1) 657 | 658 | // now, just for completeness sake we'll update the free-memory pointer accordingly 659 | mstore(0x40, freePointer) 660 | } 661 | 662 | AssertBytes.notEqual_nonAligned(memBytes1, memBytes2, "The non equality check for the non-aligned equality 4-bytes-long test failed."); 663 | } 664 | 665 | function testEqualNonAligned33Bytes() public { 666 | bytes memory memBytes1; // hex"f00d00000000000000000000000000000000000000000000000000000000feedcc"; 667 | bytes memory memBytes2; // hex"f00d00000000000000000000000000000000000000000000000000000000feedcc"; 668 | 669 | // We need to make sure that the bytes are not aligned to a 32 byte boundary 670 | // so we need to use assembly to allocate the bytes in contiguous memory 671 | // Solidity will not let us do this normally, this equality method exists 672 | // to test the edge case of non-aligned bytes created in assembly 673 | assembly { 674 | // Fetch free memory pointer 675 | let freePointer := mload(0x40) 676 | 677 | // We first store the length of the byte array (33 bytes) 678 | // And then we write a word and then a byte 679 | memBytes1 := freePointer 680 | mstore(freePointer, 0x21) 681 | freePointer := add(freePointer, 0x20) 682 | mstore(freePointer, 0xf00d00000000000000000000000000000000000000000000000000000000feed) 683 | freePointer := add(freePointer, 0x20) 684 | mstore8(freePointer, 0xcc) 685 | freePointer := add(freePointer, 0x1) 686 | 687 | // We do the same for memBytes2 in contiguous memory 688 | memBytes2 := freePointer 689 | mstore(freePointer, 0x21) 690 | freePointer := add(freePointer, 0x20) 691 | mstore(freePointer, 0xf00d00000000000000000000000000000000000000000000000000000000feed) 692 | freePointer := add(freePointer, 0x20) 693 | mstore8(freePointer, 0xcc) 694 | freePointer := add(freePointer, 0x1) 695 | 696 | // We add some garbage bytes in contiguous memory 697 | mstore8(freePointer, 0xde) 698 | freePointer := add(freePointer, 0x1) 699 | mstore8(freePointer, 0xad) 700 | freePointer := add(freePointer, 0x1) 701 | 702 | // now, just for completeness sake we'll update the free-memory pointer accordingly 703 | mstore(0x40, freePointer) 704 | } 705 | 706 | AssertBytes.equal_nonAligned(memBytes1, memBytes2, "The equality check for the non-aligned equality 33-bytes-long test failed."); 707 | // The equality check for aligned byte arrays should fail for non-aligned bytes 708 | AssertBytes.notEqual(memBytes1, memBytes2, "The equality check for the non-aligned equality 4-bytes-long test failed."); 709 | } 710 | 711 | function testEqualNonAligned33BytesFail() public { 712 | bytes memory memBytes1; // hex"f00d00000000000000000000000000000000000000000000000000000000feedcc"; 713 | bytes memory memBytes2; // hex"f00d00000000000000000000000000000000000000000000000000000000feedee"; 714 | 715 | // We need to make sure that the bytes are not aligned to a 32 byte boundary 716 | // so we need to use assembly to allocate the bytes in contiguous memory 717 | // Solidity will not let us do this normally, this equality method exists 718 | // to test the edge case of non-aligned bytes created in assembly 719 | assembly { 720 | // Fetch free memory pointer 721 | let freePointer := mload(0x40) 722 | 723 | // We first store the length of the byte array (33 bytes) 724 | // And then we write a word and then a byte 725 | memBytes1 := freePointer 726 | mstore(freePointer, 0x21) 727 | freePointer := add(freePointer, 0x20) 728 | mstore(freePointer, 0xf00d00000000000000000000000000000000000000000000000000000000feed) 729 | freePointer := add(freePointer, 0x20) 730 | mstore8(freePointer, 0xcc) 731 | freePointer := add(freePointer, 0x1) 732 | 733 | // We do the same for memBytes2 in contiguous memory 734 | memBytes2 := freePointer 735 | mstore(freePointer, 0x21) 736 | freePointer := add(freePointer, 0x20) 737 | mstore(freePointer, 0xf00d00000000000000000000000000000000000000000000000000000000feed) 738 | freePointer := add(freePointer, 0x20) 739 | mstore8(freePointer, 0xee) 740 | freePointer := add(freePointer, 0x1) 741 | 742 | // We add some garbage bytes in contiguous memory 743 | mstore8(freePointer, 0xde) 744 | freePointer := add(freePointer, 0x1) 745 | mstore8(freePointer, 0xad) 746 | freePointer := add(freePointer, 0x1) 747 | 748 | // now, just for completeness sake we'll update the free-memory pointer accordingly 749 | mstore(0x40, freePointer) 750 | } 751 | 752 | AssertBytes.notEqual_nonAligned(memBytes1, memBytes2, "The non equality check for the non-aligned equality 33-bytes-long test failed."); 753 | } 754 | 755 | /** 756 | * Edge Cases 757 | */ 758 | 759 | function testConcatMemoryZeroLength() public { 760 | // Initialize `bytes` variables in memory with different critical sizes 761 | bytes memory preZeroLength = hex""; 762 | bytes memory postZeroLength = hex""; 763 | 764 | bytes memory testBytes; 765 | bytes memory resultBytes; 766 | 767 | resultBytes = preZeroLength.concat(postZeroLength); 768 | testBytes = hex""; 769 | assertEq(resultBytes, testBytes, "Zero Length concatenation failed."); 770 | AssertBytes.equal(resultBytes, testBytes, "Zero Length concatenation failed."); 771 | } 772 | 773 | /** 774 | * Helper Functions 775 | */ 776 | 777 | function resetStorage() internal { 778 | storageBytes4 = hex"f00dfeed"; 779 | storageBytes31 = hex"f00d000000000000000000000000000000000000000000000000000000feed"; 780 | storageBytes32 = hex"f00d00000000000000000000000000000000000000000000000000000000feed"; 781 | storageBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 782 | storageBytes63 = hex"f00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 783 | storageBytes64 = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 784 | storageBytes65 = hex"f00d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 785 | storageBytes70 = hex"f00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000feed"; 786 | } 787 | } 788 | -------------------------------------------------------------------------------- /test/TestBytesLib2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../contracts/AssertBytes.sol"; 6 | import "../contracts/BytesLib.sol"; 7 | 8 | 9 | contract TestBytesLib2 is Test { 10 | using BytesLib for bytes; 11 | 12 | bytes storageCheckBytes = hex"aabbccddeeff"; 13 | bytes storageCheckBytesZeroLength = hex""; 14 | 15 | uint256 constant MAX_UINT = type(uint256).max; 16 | 17 | /** 18 | * Sanity Checks 19 | */ 20 | 21 | function testSanityCheck() public { 22 | // Assert library sanity checks 23 | // 24 | // Please don't change the ordering of the var definitions 25 | // the order is purposeful for testing zero-length arrays 26 | bytes memory checkBytes = hex"aabbccddeeff"; 27 | bytes memory checkBytesZeroLength = hex""; 28 | 29 | bytes memory checkBytesRight = hex"aabbccddeeff"; 30 | bytes memory checkBytesZeroLengthRight = hex""; 31 | bytes memory checkBytesWrongLength = hex"aa0000"; 32 | bytes memory checkBytesWrongContent = hex"aabbccddee00"; 33 | 34 | assertEq(checkBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out."); 35 | assertTrue(keccak256(checkBytes) != keccak256(checkBytesWrongLength), "Sanity check should be checking different length bytes arrays out."); 36 | assertTrue(keccak256(checkBytes) != keccak256(checkBytesWrongContent), "Sanity check should be checking different content bytes arrays out."); 37 | AssertBytes.equal(checkBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out."); 38 | AssertBytes.notEqual(checkBytes, checkBytesWrongLength, "Sanity check should be checking different length bytes arrays out."); 39 | AssertBytes.notEqual(checkBytes, checkBytesWrongContent, "Sanity check should be checking different content bytes arrays out."); 40 | 41 | assertEq(storageCheckBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out. (Storage)"); 42 | assertTrue(keccak256(storageCheckBytes) != keccak256(checkBytesWrongLength), "Sanity check should be checking different length bytes arrays out. (Storage)"); 43 | assertTrue(keccak256(storageCheckBytes) != keccak256(checkBytesWrongContent), "Sanity check should be checking different content bytes arrays out. (Storage)"); 44 | AssertBytes.equalStorage(storageCheckBytes, checkBytesRight, "Sanity check should be checking equal bytes arrays out. (Storage)"); 45 | AssertBytes.notEqualStorage(storageCheckBytes, checkBytesWrongLength, "Sanity check should be checking different length bytes arrays out. (Storage)"); 46 | AssertBytes.notEqualStorage(storageCheckBytes, checkBytesWrongContent, "Sanity check should be checking different content bytes arrays out. (Storage)"); 47 | 48 | // Zero-length checks 49 | assertEq(checkBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out."); 50 | assertTrue(keccak256(checkBytesZeroLength) != keccak256(checkBytes), "Sanity check should be checking different length bytes arrays out."); 51 | AssertBytes.equal(checkBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out."); 52 | AssertBytes.notEqual(checkBytesZeroLength, checkBytes, "Sanity check should be checking different length bytes arrays out."); 53 | 54 | AssertBytes.equalStorage(storageCheckBytesZeroLength, checkBytesZeroLengthRight, "Sanity check should be checking equal zero-length bytes arrays out. (Storage)"); 55 | AssertBytes.notEqualStorage(storageCheckBytesZeroLength, checkBytes, "Sanity check should be checking different length bytes arrays out. (Storage)"); 56 | } 57 | 58 | /** 59 | * Slice Tests 60 | */ 61 | 62 | function testSlice() public { 63 | bytes memory memBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000feed"; 64 | 65 | bytes memory testBytes; 66 | bytes memory resultBytes; 67 | 68 | testBytes = hex"f00d"; 69 | resultBytes = memBytes.slice(0,2); 70 | assertEq(resultBytes, testBytes); 71 | AssertBytes.equal(resultBytes, testBytes, "Normal slicing array failed."); 72 | 73 | testBytes = hex""; 74 | resultBytes = memBytes.slice(1,0); 75 | assertEq(resultBytes, testBytes); 76 | AssertBytes.equal(resultBytes, testBytes, "Slicing with zero-length failed."); 77 | 78 | testBytes = hex""; 79 | resultBytes = memBytes.slice(0,0); 80 | assertEq(resultBytes, testBytes); 81 | AssertBytes.equal(resultBytes, testBytes, "Slicing with zero-length on index 0 failed."); 82 | 83 | testBytes = hex"feed"; 84 | resultBytes = memBytes.slice(31,2); 85 | assertEq(resultBytes, testBytes); 86 | AssertBytes.equal(resultBytes, testBytes, "Slicing across the 32-byte slot boundary failed."); 87 | 88 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 89 | resultBytes = memBytes.slice(0,33); 90 | assertEq(resultBytes, testBytes); 91 | AssertBytes.equal(resultBytes, testBytes, "Full length slice failed."); 92 | 93 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000fe"; 94 | resultBytes = memBytes.slice(0,32); 95 | assertEq(resultBytes, testBytes); 96 | AssertBytes.equal(resultBytes, testBytes, "Multiple of 32 bytes slice failed."); 97 | 98 | testBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000feedf00d00000000000000000000000000000000000000000000000000000000fe"; 99 | resultBytes = memBytes.slice(0,64); 100 | assertEq(resultBytes, testBytes); 101 | AssertBytes.equal(resultBytes, testBytes, "Multiple (*2) of 32 bytes slice failed."); 102 | 103 | // With v0.5.x we can now entirely replace the ThrowProxy patterns that was creating issues with the js-vm 104 | // and use an external call to our own contract with the function selector, since Solidity now gives us 105 | // access to those 106 | bool r; 107 | 108 | // We're basically calling our contract externally with a raw call, forwarding all available gas, with 109 | // msg.data equal to the throwing function selector that we want to be sure throws and using only the boolean 110 | // value associated with the message call's success 111 | (r, ) = address(this).call(abi.encodePacked(this.sliceIndexThrow.selector)); 112 | assertFalse(r); 113 | 114 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowLength0Throw.selector)); 115 | assertFalse(r); 116 | 117 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowLength1Throw.selector)); 118 | assertFalse(r); 119 | 120 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowLength33Throw.selector)); 121 | assertFalse(r); 122 | 123 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowLengthMinus32Throw.selector)); 124 | assertFalse(r); 125 | 126 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowStart0Throw.selector)); 127 | assertFalse(r); 128 | 129 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowStart1Throw.selector)); 130 | assertFalse(r); 131 | 132 | (r, ) = address(this).call(abi.encodePacked(this.sliceOverflowStart33Throw.selector)); 133 | assertFalse(r); 134 | 135 | (r, ) = address(this).call(abi.encodePacked(this.sliceLengthThrow.selector)); 136 | assertFalse(r); 137 | } 138 | 139 | function sliceIndexThrow() public pure { 140 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 141 | 142 | bytes memory testBytes; 143 | bytes memory resultBytes; 144 | 145 | testBytes = hex"f00d"; 146 | resultBytes = memBytes33.slice(34,2); 147 | // This should throw; 148 | } 149 | 150 | function sliceLengthThrow() public pure { 151 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 152 | 153 | bytes memory testBytes; 154 | bytes memory resultBytes; 155 | 156 | testBytes = hex"f00d"; 157 | resultBytes = memBytes33.slice(0,34); 158 | // This should throw; 159 | } 160 | 161 | function sliceOverflowLength0Throw() public pure { 162 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 163 | 164 | bytes memory testBytes; 165 | bytes memory resultBytes; 166 | 167 | testBytes = hex"f00d"; 168 | resultBytes = memBytes33.slice(0, MAX_UINT); 169 | // This should throw; 170 | } 171 | 172 | function sliceOverflowLength1Throw() public pure { 173 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 174 | 175 | bytes memory testBytes; 176 | bytes memory resultBytes; 177 | 178 | testBytes = hex"f00d"; 179 | resultBytes = memBytes33.slice(1, MAX_UINT); 180 | // This should throw; 181 | } 182 | 183 | function sliceOverflowLength33Throw() public pure { 184 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 185 | 186 | bytes memory testBytes; 187 | bytes memory resultBytes; 188 | 189 | testBytes = hex"f00d"; 190 | resultBytes = memBytes33.slice(33, MAX_UINT); 191 | // This should throw; 192 | } 193 | 194 | function sliceOverflowLengthMinus32Throw() public pure { 195 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 196 | 197 | bytes memory testBytes; 198 | bytes memory resultBytes; 199 | 200 | testBytes = hex"f00d"; 201 | resultBytes = memBytes33.slice(1, MAX_UINT - 32); 202 | // This should throw; 203 | } 204 | 205 | function sliceOverflowStart0Throw() public pure { 206 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 207 | 208 | bytes memory testBytes; 209 | bytes memory resultBytes; 210 | 211 | testBytes = hex"f00d"; 212 | resultBytes = memBytes33.slice(MAX_UINT, 0); 213 | // This should throw; 214 | } 215 | 216 | function sliceOverflowStart1Throw() public pure { 217 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 218 | 219 | bytes memory testBytes; 220 | bytes memory resultBytes; 221 | 222 | testBytes = hex"f00d"; 223 | resultBytes = memBytes33.slice(MAX_UINT, 1); 224 | // This should throw; 225 | } 226 | 227 | function sliceOverflowStart33Throw() public pure { 228 | bytes memory memBytes33 = hex"f00d0000000000000000000000000000000000000000000000000000000000feed"; 229 | 230 | bytes memory testBytes; 231 | bytes memory resultBytes; 232 | 233 | testBytes = hex"f00d"; 234 | resultBytes = memBytes33.slice(MAX_UINT, 1); 235 | // This should throw; 236 | } 237 | 238 | /** 239 | * Memory Integrity Checks 240 | */ 241 | 242 | function testMemoryIntegrityCheckZeroLengthSlice() public { 243 | // Let's taint memory first 244 | bytes memory taintBytes4 = hex"f00dfeed"; 245 | 246 | // Now declare arrays to be sliced 247 | bytes memory testBytes4 = hex"f00dfeed"; 248 | bytes memory emptyBytes = hex""; 249 | 250 | bytes memory resultBytes; 251 | 252 | // Try a zero-length slice from a non-zero-length array 253 | resultBytes = testBytes4.slice(0,0); 254 | 255 | assertEq(hex"", resultBytes); 256 | AssertBytes.equal(hex"", resultBytes, "The result of a zero-length slice is not a zero-length array."); 257 | 258 | // Try a zero-length slice from a zero-length array 259 | resultBytes = emptyBytes.slice(0,0); 260 | 261 | assertEq(hex"", resultBytes); 262 | AssertBytes.equal(hex"", resultBytes, "The result of a zero-length slice is not a zero-length array."); 263 | } 264 | 265 | /** 266 | * Type casting Checks 267 | */ 268 | 269 | function testToUint8() public { 270 | bytes memory memBytes = hex"f00d20feed"; 271 | 272 | uint8 testUint8 = 32; // 0x20 == 32 273 | uint8 resultUint8; 274 | 275 | resultUint8 = memBytes.toUint8(2); 276 | assertEq(uint256(resultUint8), uint256(testUint8)); 277 | 278 | // Testing for the throw conditions below 279 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint8Throw.selector)); 280 | assertFalse(r); 281 | } 282 | 283 | function toUint8Throw() public pure { 284 | bytes memory memBytes = hex"f00d42feed"; 285 | 286 | uint8 resultUint8; 287 | 288 | resultUint8 = memBytes.toUint8(35); 289 | // This should throw; 290 | } 291 | 292 | function testToUint16() public { 293 | bytes memory memBytes = hex"f00d0020feed"; 294 | 295 | uint16 testUint16 = 32; // 0x20 == 32 296 | uint16 resultUint16; 297 | 298 | resultUint16 = memBytes.toUint16(2); 299 | assertEq(uint256(resultUint16), uint256(testUint16)); 300 | 301 | // Testing for the throw conditions below 302 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint16Throw.selector)); 303 | assertFalse(r); 304 | } 305 | 306 | function toUint16Throw() public pure { 307 | bytes memory memBytes = hex"f00d0042feed"; 308 | 309 | uint16 resultUint16; 310 | 311 | resultUint16 = memBytes.toUint16(35); 312 | // This should throw; 313 | } 314 | 315 | function testToUint32() public { 316 | bytes memory memBytes = hex"f00d00000020feed"; 317 | 318 | uint32 testUint32 = 32; // 0x20 == 32 319 | uint32 resultUint32; 320 | 321 | resultUint32 = memBytes.toUint32(2); 322 | assertEq(uint256(resultUint32), uint256(testUint32)); 323 | 324 | // Testing for the throw conditions below 325 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint32Throw.selector)); 326 | assertFalse(r); 327 | } 328 | 329 | function toUint32Throw() public pure { 330 | bytes memory memBytes = hex"f00d00000042feed"; 331 | 332 | uint32 resultUint32; 333 | 334 | resultUint32 = memBytes.toUint32(35); 335 | // This should throw; 336 | } 337 | 338 | function testToUint64() public { 339 | bytes memory memBytes = hex"f00d0000000000000020feed"; 340 | 341 | uint64 testUint64 = 32; // 0x20 == 32 342 | uint64 resultUint64; 343 | 344 | resultUint64 = memBytes.toUint64(2); 345 | assertEq(uint256(resultUint64), uint256(testUint64)); 346 | 347 | // Testing for the throw conditions below 348 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint64Throw.selector)); 349 | assertFalse(r); 350 | } 351 | 352 | function toUint64Throw() public pure { 353 | bytes memory memBytes = hex"f00d42feed"; 354 | 355 | uint64 resultUint64; 356 | 357 | resultUint64 = memBytes.toUint64(35); // This should throw; 358 | } 359 | 360 | function testToUint96() public { 361 | bytes memory memBytes = hex"f00d000000000000000000000020feed"; 362 | 363 | uint96 testUint96 = 32; // 0x20 == 32 364 | uint96 resultUint96; 365 | 366 | resultUint96 = memBytes.toUint96(2); 367 | assertEq(uint256(resultUint96), uint256(testUint96)); 368 | 369 | // Testing for the throw conditions below 370 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint64Throw.selector)); 371 | assertFalse(r); 372 | } 373 | 374 | function toUint96Throw() public pure { 375 | bytes memory memBytes = hex"f00d42feed"; 376 | 377 | uint96 resultUint96; 378 | 379 | resultUint96 = memBytes.toUint96(35); // This should throw; 380 | } 381 | 382 | function testToUint128() public { 383 | bytes memory memBytes = hex"f00d00000000000000000000000000000020feed"; 384 | 385 | uint128 testUint128 = 32; // 0x20 == 32 386 | uint128 resultUint128; 387 | 388 | resultUint128 = memBytes.toUint128(2); 389 | assertEq(uint256(resultUint128), uint256(testUint128)); 390 | 391 | // Testing for the throw conditions below 392 | (bool r, ) = address(this).call(abi.encodePacked(this.toUint128Throw.selector)); 393 | assertFalse(r); 394 | } 395 | 396 | function toUint128Throw() public pure { 397 | bytes memory memBytes = hex"f00d42feed"; 398 | 399 | uint128 resultUint128; 400 | 401 | resultUint128 = memBytes.toUint128(35); // This should throw; 402 | } 403 | 404 | function testToUint() public { 405 | bytes memory memBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000020feed"; 406 | 407 | uint256 testUint = 32; // 0x20 == 32 408 | uint256 resultUint; 409 | 410 | resultUint = memBytes.toUint256(2); 411 | assertEq(resultUint, testUint); 412 | 413 | // Testing for the throw conditions below 414 | (bool r, ) = address(this).call(abi.encodePacked(this.toUintThrow.selector)); 415 | assertFalse(r); 416 | } 417 | 418 | function toUintThrow() public pure { 419 | bytes memory memBytes = hex"f00d0000000000000000000000000000000000000000000000000000000000000042feed"; 420 | 421 | uint256 resultUint; 422 | 423 | resultUint = memBytes.toUint256(35); 424 | // This should throw; 425 | } 426 | 427 | function testToAddress() public { 428 | bytes memory memBytes = hex"f00dfeed383Fa3B60f9B4AB7fBf6835d3c26C3765cD2B2e2f00dfeed"; 429 | 430 | address testAddress = 0x383Fa3B60f9B4AB7fBf6835d3c26C3765cD2B2e2; 431 | address resultAddress; 432 | 433 | resultAddress = memBytes.toAddress(4); 434 | assertEq(resultAddress, testAddress); 435 | 436 | // Testing for the throw conditions below 437 | (bool r, ) = address(this).call(abi.encodePacked(this.toAddressThrow.selector)); 438 | assertFalse(r); 439 | } 440 | 441 | function toAddressThrow() public pure { 442 | bytes memory memBytes = hex"f00dfeed383FA3b60F9b4ab7FBF6835D3c26C3765Cd2B2E2f00dfeed"; 443 | 444 | address resultAddress; 445 | 446 | resultAddress = memBytes.toAddress(35); 447 | // This should throw; 448 | } 449 | } 450 | --------------------------------------------------------------------------------