Last active
July 28, 2022 06:08
-
-
Save Agusx1211/e2f6743d8886c784843de5e95b99da78 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.11; | |
/* | |
* @author Agustin Aguilar <@agusx1211> | |
*/ | |
library ChunkStorage { | |
struct Storage { | |
mapping(bytes32 => address) slots; | |
} | |
function write( | |
Storage storage _space, | |
bytes32 _key, | |
bytes memory _data | |
) internal { | |
address slot; | |
if (_data.length == 0) { | |
delete _space.slots[_key]; | |
return; | |
} | |
assembly { | |
// Load data to store, size and offset | |
let data_size := mload(_data) | |
let data_offset := add(_data, 0x20) | |
// Calculate expected data-contract size | |
let known_size | |
// If the contract is going to need PUSH2 | |
// we need to account for that during the size estimation | |
let one_byte_push_only := lt(data_size, 289) | |
if one_byte_push_only { | |
// Calculate number of words required for storing | |
// the data | |
let words := div(data_size, 0x20) | |
// divRound number of words, if we need to store half word | |
// it's the same as storing a full one | |
if iszero(iszero(mod(data_size, 0x20))){ | |
words := add(words, 0x01) | |
} | |
// Each word takes 0x24 bytes to store (PUSH 32 + 32 bytes + PUSH1 + 1 byte + MSTORE) | |
// and we need 0x06 extra bytes for the "return" opcodes | |
known_size := add(0x06, mul(words, 0x24)) | |
} | |
if iszero(one_byte_push_only) { | |
// We know that 0x120 bytes of the data are stored using | |
// PUSH1, and thus that size is fixed, we need to calculate | |
// the remaining size | |
let tmp_data_size := sub(data_size, 0x120) | |
let words := div(tmp_data_size, 0x20) | |
// divRound number of words, if we need to store half word | |
// it's the same as storing a full one | |
if iszero(iszero(mod(tmp_data_size, 0x20))){ | |
words := add(words, 0x01) | |
} | |
// Each word takes 0x25 bytes to store (PUSH 32 + 32 bytes + PUSH2 + 2 bytes + MSTORE) | |
// the 0x06 extra bytes for the "return" opcodes and | |
// we need to add the 0x145 bytes of the PUSH1 data | |
known_size := add(0x145, add(0x06, mul(words, 0x25))) | |
// We add the 0x09 words of the PUSH1 data | |
words := add(0x09, words) | |
} | |
// Get free memory to store the generated bytecode | |
let bytecode_offset := mload(0x40) | |
// Contract constructor, adding the calculated size of the contract | |
mstore(bytecode_offset, or(shl(232, known_size), 0x610000600081600B8239F3000000000000000000000000000000000000000000)) | |
// Create the bytecode pointer to start writting the contract | |
let bytecode_pointer := add(0x0b, bytecode_offset) | |
// Iteare the data array, using 32 bytes words | |
for { let data_c := 0 } lt(data_c, data_size) { } { | |
// Write PUSH32 opcode and update pointer | |
mstore(bytecode_pointer, 0x7f00000000000000000000000000000000000000000000000000000000000000) | |
bytecode_pointer := add(bytecode_pointer, 0x01) | |
// Copy data to bytecode, 32 bytes | |
mstore(bytecode_pointer, mload(add(data_offset, data_c))) | |
bytecode_pointer := add(bytecode_pointer, 0x20) | |
// Check if the MSTORE opcode uses a PUSH1 or a PUSH2 | |
let one_byte_opcode := lt(data_c, 0x0100) | |
if one_byte_opcode { | |
// Write PUSH1 [data_c] MSTORE and update pointer | |
mstore(bytecode_pointer, or(shl(0xf0, data_c), 0x6000520000000000000000000000000000000000000000000000000000000000)) | |
bytecode_pointer := add(bytecode_pointer, 0x03) | |
} | |
if iszero(one_byte_opcode) { | |
// Write PUSH2 [data_c] MSTORE and update pointer | |
mstore(bytecode_pointer, or(shl(0xe8, data_c), 0x6100005200000000000000000000000000000000000000000000000000000000)) | |
bytecode_pointer := add(bytecode_pointer, 0x04) | |
} | |
// Move the data pointer 32 bytes | |
data_c := add(data_c, 0x20) | |
} | |
// Write return function for the data-contract code and update the pointer | |
mstore(bytecode_pointer, or(shl(232, data_size), 0x6100006000f30000000000000000000000000000000000000000000000000000)) | |
bytecode_pointer := add(bytecode_pointer, 0x06) | |
// Execute create using the generated bytecode | |
slot := create(0, bytecode_offset, sub(bytecode_pointer, bytecode_offset)) | |
} | |
_space.slots[_key] = slot; | |
} | |
function read(Storage storage _space, bytes32 _key) internal view returns (bytes memory _data) { | |
address slot = _space.slots[_key]; | |
if (slot == address(0)) { | |
_data = bytes(""); | |
} else { | |
(, _data) = slot.staticcall(""); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment