diff --git a/contracts/v3/EnumerableSet.sol b/contracts/v3/EnumerableSet.sol new file mode 100644 index 0000000..f24ddd4 --- /dev/null +++ b/contracts/v3/EnumerableSet.sol @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) +// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. + +pragma solidity ^0.8.0; + +/** + * @dev Library for managing + * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive + * types. + * + * Sets have the following properties: + * + * - Elements are added, removed, and checked for existence in constant time + * (O(1)). + * - Elements are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableSet for EnumerableSet.AddressSet; + * + * // Declare a set state variable + * EnumerableSet.AddressSet private mySet; + * } + * ``` + * + * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) + * and `uint256` (`UintSet`) are supported. + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableSet. + * ==== + */ +library EnumerableSet { + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Set type with + // bytes32 values. + // The Set implementation uses private functions, and user-facing + // implementations (such as AddressSet) are just wrappers around the + // underlying Set. + // This means that we can only create new EnumerableSets for types that fit + // in bytes32. + + struct Set { + // Storage of set values + bytes32[] _values; + // Position of the value in the `values` array, plus 1 because index 0 + // means a value is not in the set. + mapping(bytes32 => uint256) _indexes; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _add(Set storage set, bytes32 value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } + } + + /** + * @dev replace a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _replace(Set storage set, uint256 index, bytes32 oldValue , bytes32 newValue) private returns (bool) { + if (set._values[index-1] != oldValue){ + return false; + } + set._indexes[oldValue] = 0; + // 0 represents the value is empty + set._indexes[newValue] = index; + set._values[index-1] = newValue; + return true; + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function _remove(Set storage set, bytes32 value) private returns (bool) { + // We read and store the value's index to prevent multiple reads from the same storage slot + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + bytes32 lastValue = set._values[lastIndex]; + + // Move the last value to the index where the value to delete is + set._values[toDeleteIndex] = lastValue; + // Update the index for the moved value + set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex + } + + // Delete the slot where the moved value was stored + set._values.pop(); + + // Delete the index for the deleted slot + delete set._indexes[value]; + + return true; + } else { + return false; + } + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function _contains(Set storage set, bytes32 value) private view returns (bool) { + return set._indexes[value] != 0; + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function _length(Set storage set) private view returns (uint256) { + return set._values.length; + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Set storage set, uint256 index) private view returns (bytes32) { + return set._values[index]; + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function _values(Set storage set) private view returns (bytes32[] memory) { + return set._values; + } + + // Bytes32Set + + struct Bytes32Set { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _add(set._inner, value); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _remove(set._inner, value); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { + return _contains(set._inner, value); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(Bytes32Set storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { + return _at(set._inner, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { + bytes32[] memory store = _values(set._inner); + bytes32[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // AddressSet + + struct AddressSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(AddressSet storage set, address value) internal returns (bool) { + return _add(set._inner, bytes32(uint256(uint160(value)))); + } + + function replace(AddressSet storage set,uint256 index, address oldValue, address newValue) internal returns (bool) { + return _replace(set._inner, index ,bytes32(uint256(uint160(oldValue))), bytes32(uint256(uint160(newValue)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(AddressSet storage set, address value) internal returns (bool) { + return _remove(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(AddressSet storage set, address value) internal view returns (bool) { + return _contains(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(AddressSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressSet storage set, uint256 index) internal view returns (address) { + return address(uint160(uint256(_at(set._inner, index)))); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(AddressSet storage set) internal view returns (address[] memory) { + bytes32[] memory store = _values(set._inner); + address[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // UintSet + + struct UintSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(UintSet storage set, uint256 value) internal returns (bool) { + return _add(set._inner, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(UintSet storage set, uint256 value) internal returns (bool) { + return _remove(set._inner, bytes32(value)); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(UintSet storage set, uint256 value) internal view returns (bool) { + return _contains(set._inner, bytes32(value)); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(UintSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintSet storage set, uint256 index) internal view returns (uint256) { + return uint256(_at(set._inner, index)); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(UintSet storage set) internal view returns (uint256[] memory) { + bytes32[] memory store = _values(set._inner); + uint256[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } +} diff --git a/contracts/v3/Hub.sol b/contracts/v3/Hub.sol index d79b4f8..29a7948 100644 --- a/contracts/v3/Hub.sol +++ b/contracts/v3/Hub.sol @@ -5,21 +5,38 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; -import "./Repository.sol"; +import "./EnumerableSet.sol"; +import "./Repositorylib.sol"; +import "./database/database.sol"; contract Hub is AccessControlEnumerableUpgradeable{ - // AccessController public accessController; - mapping(bytes=>address) public nameToRepository; - bytes[] public repoNames; + using EnumerableSet for EnumerableSet.AddressSet; + using RepositoryLib for RepositoryLib.BranchInfo; - bytes32 public constant CREATOR = bytes32(uint256(1)); - bytes32 public constant MANAGER = bytes32(uint256(2)); - bytes32 public constant CONTRIBUTOR = bytes32(uint256(3)); + // Hub Info + bytes32 public constant CREATOR = bytes32(uint256(0)); + bytes32 public constant MANAGER = bytes32(uint256(1)); + bytes32 public constant CONTRIBUTOR = bytes32(uint256(2)); bytes32[] public RoleList = [CREATOR,MANAGER,CONTRIBUTOR]; - // mapping(bytes32=>address)public executors; - bool public permissionless; + // Repository Info + struct RepositoryInfo{ + uint256 repoNameIndex; + address owner; + bool exist; + EnumerableSet.AddressSet repoContributors; + RepositoryLib.BranchInfo branchs; + } + + mapping(bytes=>RepositoryInfo) nameToRepository; + bytes[] public repoNames; + + // DataBase Info + database public db; + + + // ===== hub operator functions====== function openPermissonlessJoin(bool open) public { require(hasRole(CREATOR, _msgSender())); permissionless = open; @@ -38,8 +55,8 @@ contract Hub is AccessControlEnumerableUpgradeable{ } //createRepository can be invoked by anyone within Hub - function createRepository(bytes memory repoName) public returns(address){ - require(hasRole(CREATOR, _msgSender())); + function createRepository(bytes memory repoName) public{ + require(memberShip()); require( repoName.length > 0 && repoName.length <= 100, "RepoName length must be 1-100" @@ -55,22 +72,22 @@ contract Hub is AccessControlEnumerableUpgradeable{ ); } + RepositoryInfo storage repo = nameToRepository[repoName]; require( - nameToRepository[repoName] == address(0), + repo.exist == false , "RepoName already exist" ); - address repo = address(new Repository(repoName)); - nameToRepository[repoName] = repo; + + repo.repoNameIndex = repoNames.length; + repo.owner = _msgSender(); + repo.exist = true; repoNames.push(repoName); - return repo; } function deleteRepository(bytes memory repoName) public returns(address){ - require(nameToRepository[repoName]!=address(0),"repoName do not exist"); - address repoAddr = nameToRepository[repoName]; + require(hasRole(CREATOR, _msgSender()) || hasRole(MANAGER, _msgSender())); + require(nameToRepository[repoName].exist==true,"repoName do not exist"); delete(nameToRepository[repoName]); - // todo:remove repoName from repoNames - return repoAddr; } function addMember( bytes32 senderRole , bytes32 role,address member) public{ @@ -81,7 +98,6 @@ contract Hub is AccessControlEnumerableUpgradeable{ function deleteMember(bytes32 senderRole ,bytes32 role,address member) public{ - require(hasRole(CREATOR, _msgSender())); require(hasRole(senderRole, _msgSender())); require(senderRole>role); revokeRole(role, member); @@ -93,5 +109,58 @@ contract Hub is AccessControlEnumerableUpgradeable{ grantRole(CONTRIBUTOR, _msgSender()); } + // ===== repository operator functions====== + + function isRepoContributor(bytes memory repoName , address member) internal view returns(bool){ + RepositoryInfo storage repo = nameToRepository[repoName]; + if (repo.owner == member) return true; + if (repo.repoContributors.contains(member)) { + return true; + }else { + return false; + } + } + + function listRepoBranchs( bytes memory repoName)external view { + nameToRepository[repoName].branchs.listBranchs(); + } + + function updateRepoBranch( + bytes memory repoName, + bytes memory branchPath, + bytes20 refHash + )external{ + require(isRepoContributor(repoName, _msgSender())); + nameToRepository[repoName].branchs.updateBranch(repoName,branchPath,refHash); + } + + function removeRepoBranch( + bytes memory repoName, + bytes memory branchPath + ) external{ + require(isRepoContributor(repoName, _msgSender())); + nameToRepository[repoName].branchs.removeBranch(repoName,branchPath); + } + + // ===== database operator functions====== + function newDataBase() external onlyRole(CREATOR) { + + } + + function download( + bytes memory repoName, + bytes memory path + ) external view returns (bytes memory, bool) { + // call flat directory(FD) + return db.download(repoName, path); + } + + function upload( + bytes memory repoName, + bytes memory path, + bytes calldata data + ) external payable { + return db.upload(repoName, path,data); + } } diff --git a/contracts/v3/HubFactory.sol b/contracts/v3/HubFactory.sol deleted file mode 100644 index 536fdfd..0000000 --- a/contracts/v3/HubFactory.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.8.0; - -// import "hardhat/console.sol"; -// import "@openzeppelin/contracts/access/Ownable.sol"; -import "./Hub.sol"; - -contract HubFactory { - function createHub() public returns(Hub){ - Hub hub = new Hub(); - return hub; - } -} \ No newline at end of file diff --git a/contracts/v3/Repository.sol b/contracts/v3/Repository.sol deleted file mode 100644 index 4e7c0ab..0000000 --- a/contracts/v3/Repository.sol +++ /dev/null @@ -1,111 +0,0 @@ -pragma solidity ^0.8.0; - -import "./storage/IStorageLayer.sol"; -import "./RepositoryAccess.sol"; - -contract Repository is RepositoryAccess{ - - struct refInfo { - bytes20 hash; - uint96 index; - } - - struct refData { - bytes20 hash; - bytes name; - } - - bytes repositoryName; - address creator; - address[] public contributorList; - - mapping(bytes => refInfo) public branchToRefInfo; // dev => {hash: 0x1234..., index: 1 } - bytes[] public branchs; // 有几条branch,就有几个reference - - IStorageLayer public storageManager; - bytes32 constant public ETHSTORAGEID_LAYER = bytes32(keccak256("ETHSTORAGE")); - bytes32 constant public NFTSTORAGE_LAYER = bytes32(keccak256("NFTSTORAGE")); - constructor(bytes memory repoName){ - creator = msg.sender; - repositoryName = repoName; - } - - modifier onlyCreator() { - require(address(storageManager) == msg.sender, "only creator"); - _; - } - - function listBranchs() external view returns (refData[] memory list) { - list = new refData[](branchs.length); - for (uint index = 0; index < branchs.length; index++) { - list[index] = _convertToRefData( - branchToRefInfo[branchs[index]] - ); - } - } - - function createBranch(bytes memory branch,bytes20 refHash) public onlyCreator { - bytes memory fullname = bytes.concat(repositoryName,"/",branch); - require(refHash!=bytes20(0),"reference hash don't allow to set 0x0" ); - require(branchToRefInfo[fullname].hash == bytes20(0),"branch already exists"); - branchToRefInfo[fullname].hash = refHash; - branchToRefInfo[fullname].index = uint96(branchs.length); - branchs.push(fullname); - - // add branch owner - this.addBranchOperator(fullname,msg.sender); - } - - - function updateBranch( - bytes memory branch, - bytes20 refHash - )external onlyBranchOperator(branch){ - bytes memory fullname = bytes.concat(repositoryName,"/",branch); - require(refHash!=bytes20(0),"reference hash don't allow to set 0x0" ); - require(branchToRefInfo[fullname].hash != bytes20(0),"branch do not exist"); - branchToRefInfo[fullname].hash = refHash; - } - - function removeBranch( - bytes memory branch - ) external { - bytes memory fullname = bytes.concat(repositoryName,"/",branch); - refInfo memory refI = branchToRefInfo[fullname]; - require( - refI.hash != bytes20(0), - "Reference of this name does not exist" - ); - uint256 lastIndex = branchs.length -1 ; - if (refI.index < lastIndex){ - branchToRefInfo[branchs[lastIndex]].index = refI.index; - branchs[refI.index] = branchs[lastIndex]; - } - branchs.pop(); - delete branchToRefInfo[fullname]; - } - - function _convertToRefData( - refInfo memory info - ) internal view returns (refData memory res) { - res.hash = info.hash; - res.name = branchs[info.index]; - } - - // data storage module - - function setStorageLayer(IStorageLayer addr) external onlyCreator { - storageManager = addr; - } - - function upload( - bytes20 refHash, - bytes calldata data - ) external payable { - storageManager.upload(refHash, data); - } - - function download(bytes20 refHash) external view returns(bytes32 storageLayerId , bytes memory data){ - return storageManager.download(refHash); - } -} \ No newline at end of file diff --git a/contracts/v3/RepositoryAccess.sol b/contracts/v3/RepositoryAccess.sol deleted file mode 100644 index 61945ca..0000000 --- a/contracts/v3/RepositoryAccess.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; - -contract RepositoryAccess{ - using EnumerableSet for EnumerableSet.AddressSet; - mapping(bytes => EnumerableSet.AddressSet) BranchOperators; - - modifier onlyBranchOperator(bytes memory branch) { - require(BranchOperators[branch].contains(msg.sender),"only branch Operator"); - _; - } - - function _getBranchOwner(bytes memory branch) internal view returns(address){ - return BranchOperators[branch].at(0) ; - } - - function addBranchOperator(bytes memory branch, address member) external virtual{ - require(_getBranchOwner(branch) == msg.sender,"only branch owner"); - BranchOperators[branch].add(member); - } - - function removeBranchOperator(bytes memory branch , address member) external virtual{ - require(_getBranchOwner(branch) == msg.sender,"only branch owner"); - BranchOperators[branch].remove(member); - } - -} diff --git a/contracts/v3/Repositorylib.sol b/contracts/v3/Repositorylib.sol new file mode 100644 index 0000000..0f7c9ad --- /dev/null +++ b/contracts/v3/Repositorylib.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.8.0; + +library RepositoryLib { + + struct refInfo { + bytes20 hash; + uint96 index; + } + + struct refData { + bytes20 hash; + bytes name; + } + + struct BranchInfo{ + mapping(bytes => refInfo) branchToRefInfo; // dev => {hash: 0x1234..., index: 1 } + bytes[] branchs; // 有几条branch,就有几个reference + } + + + function listBranchs(BranchInfo storage info) external view returns (refData[] memory list) { + list = new refData[](info.branchs.length); + for (uint index = 0; index < info.branchs.length; index++) { + list[index] = _convertToRefData( + info, + info.branchToRefInfo[info.branchs[index]] + ); + } + } + + function updateBranch( + BranchInfo storage info, + bytes memory repositoryName, + bytes memory branch, + bytes20 refHash + ) external { + bytes memory fullname = bytes.concat(repositoryName,"/",branch); + require(refHash!=bytes20(0),"reference hash don't allow to set 0x0" ); + if (info.branchToRefInfo[fullname].hash==bytes20(0)) { + info.branchToRefInfo[fullname].hash = refHash; + info.branchToRefInfo[fullname].index = uint96(info.branchs.length); + info.branchs.push(fullname); + }else { + info.branchToRefInfo[fullname].hash = refHash; + } + + } + + function removeBranch( + BranchInfo storage info, + bytes memory repositoryName, + bytes memory branch + ) external { + bytes memory fullname = bytes.concat(repositoryName,"/",branch); + refInfo memory refI = info.branchToRefInfo[fullname]; + require( + refI.hash != bytes20(0), + "Reference of this name does not exist" + ); + uint256 lastIndex = info.branchs.length -1 ; + if (refI.index < lastIndex){ + info.branchToRefInfo[info.branchs[lastIndex]].index = refI.index; + info.branchs[refI.index] = info.branchs[lastIndex]; + } + info.branchs.pop(); + delete info.branchToRefInfo[fullname]; + } + + function _convertToRefData( + BranchInfo storage info, + refInfo memory rInfo + ) internal view returns (refData memory res) { + res.hash = rInfo.hash; + res.name = info.branchs[rInfo.index]; + } + +} \ No newline at end of file diff --git a/contracts/v3/database/EthStorage/LargeStorageManagerV2.sol b/contracts/v3/database/EthStorage/LargeStorageManagerV2.sol new file mode 100644 index 0000000..8896343 --- /dev/null +++ b/contracts/v3/database/EthStorage/LargeStorageManagerV2.sol @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./optimize/SlotHelper.sol"; +import "./StorageHelperV2.sol"; +import "./StorageSlotSelfDestructableV2.sol"; + +// Large storage manager to support arbitrarily-sized data with multiple chunk +contract LargeStorageManagerV2 { + using SlotHelper for bytes32; + using SlotHelper for address; + + uint8 internal constant SLOT_LIMIT = 0; + mapping(bytes32 => mapping(uint256 => bytes32)) internal keyToMetadata; + mapping(bytes32 => mapping(uint256 => mapping(uint256 => bytes32))) + internal keyToSlots; + + function isOptimize() public pure returns (bool) { + return SLOT_LIMIT > 0; + } + + function _preparePut(bytes32 key, uint256 chunkId) private { + bytes32 metadata = keyToMetadata[key][chunkId]; + + if (metadata == bytes32(0)) { + require( + chunkId == 0 || keyToMetadata[key][chunkId - 1] != bytes32(0x0), + "must replace or append" + ); + } + + if (!metadata.isInSlot()) { + address addr = metadata.bytes32ToAddr(); + if (addr != address(0x0)) { + // remove the KV first if it exists + StorageSlotSelfDestructableV2(addr).destruct(); + } + } + } + + function _putChunkFromCalldata( + bytes32 key, + uint256 chunkId, + bytes calldata data, + uint256 value + ) internal { + _preparePut(key, chunkId); + + // store data and rewrite metadata + if (data.length > SLOT_LIMIT) { + keyToMetadata[key][chunkId] = StorageHelperV2 + .putRawFromCalldata(data, value) + .addrToBytes32(); + } else { + keyToMetadata[key][chunkId] = SlotHelper.putRaw( + keyToSlots[key][chunkId], + data + ); + } + } + + function _putChunk( + bytes32 key, + uint256 chunkId, + bytes memory data, + uint256 value + ) internal { + _preparePut(key, chunkId); + + // store data and rewrite metadata + if (data.length > SLOT_LIMIT) { + keyToMetadata[key][chunkId] = StorageHelperV2 + .putRaw(data, value) + .addrToBytes32(); + } else { + keyToMetadata[key][chunkId] = SlotHelper.putRaw( + keyToSlots[key][chunkId], + data + ); + } + } + + function _getChunkAddr( + bytes32 key, + uint256 chunkId + ) internal view returns (address) { + bytes32 metadata = keyToMetadata[key][chunkId]; + address addr = metadata.bytes32ToAddr(); + return addr; + } + + function _getChunk( + bytes32 key, + uint256 chunkId + ) internal view returns (bytes memory, bool) { + bytes32 metadata = keyToMetadata[key][chunkId]; + + if (metadata.isInSlot()) { + bytes memory res = SlotHelper.getRaw( + keyToSlots[key][chunkId], + metadata + ); + return (res, true); + } else { + address addr = metadata.bytes32ToAddr(); + return StorageHelperV2.getRaw(addr); + } + } + + function _stakeTokens( + bytes32 key, + uint256 chunkId + ) internal view returns (uint256) { + uint256 stakeNum = 0; + + while (true) { + (uint256 count, bool found) = _chunkStakeTokens(key, chunkId); + if (!found) { + return stakeNum; + } + stakeNum += count; + chunkId++; + } + + return stakeNum; + } + + function _chunkStakeTokens( + bytes32 key, + uint256 chunkId + ) internal view returns (uint256, bool) { + bytes32 metadata = keyToMetadata[key][chunkId]; + if (metadata == bytes32(0)) { + return (0, false); + } else if (metadata.isInSlot()) { + return (0, true); + } else { + address addr = metadata.bytes32ToAddr(); + return (addr.balance, true); + } + } + + function _chunkSize( + bytes32 key, + uint256 chunkId + ) internal view returns (uint256, bool) { + bytes32 metadata = keyToMetadata[key][chunkId]; + + if (metadata == bytes32(0)) { + return (0, false); + } else if (metadata.isInSlot()) { + uint256 len = metadata.decodeLen(); + return (len, true); + } else { + address addr = metadata.bytes32ToAddr(); + return StorageHelperV2.sizeRaw(addr); + } + } + + function _countChunks(bytes32 key) internal view returns (uint256) { + uint256 chunkId = 0; + + while (true) { + bytes32 metadata = keyToMetadata[key][chunkId]; + if (metadata == bytes32(0x0)) { + break; + } + + chunkId++; + } + + return chunkId; + } + + // Returns (size, # of chunks). + function _size(bytes32 key) internal view returns (uint256, uint256) { + uint256 size = 0; + uint256 chunkId = 0; + + while (true) { + (uint256 chunkSize, bool found) = _chunkSize(key, chunkId); + if (!found) { + break; + } + + size += chunkSize; + chunkId++; + } + + return (size, chunkId); + } + + function _get(bytes32 key) internal view returns (bytes memory, bool) { + (uint256 size, uint256 chunkNum) = _size(key); + if (chunkNum == 0) { + return (new bytes(0), false); + } + + bytes memory data = new bytes(size); // solidity should auto-align the memory-size to 32 + uint256 dataPtr; + assembly { + dataPtr := add(data, 0x20) + } + for (uint256 chunkId = 0; chunkId < chunkNum; chunkId++) { + bytes32 metadata = keyToMetadata[key][chunkId]; + + uint256 chunkSize = 0; + if (metadata.isInSlot()) { + chunkSize = metadata.decodeLen(); + SlotHelper.getRawAt( + keyToSlots[key][chunkId], + metadata, + dataPtr + ); + } else { + address addr = metadata.bytes32ToAddr(); + (chunkSize, ) = StorageHelperV2.sizeRaw(addr); + StorageHelperV2.getRawAt(addr, dataPtr); + } + + dataPtr += chunkSize; + } + + return (data, true); + } + + // Returns # of chunks deleted + function _remove(bytes32 key, uint256 chunkId) internal returns (uint256) { + while (true) { + bytes32 metadata = keyToMetadata[key][chunkId]; + if (metadata == bytes32(0x0)) { + break; + } + + if (!metadata.isInSlot()) { + address addr = metadata.bytes32ToAddr(); + // remove new contract + StorageSlotSelfDestructableV2(addr).destruct(); + } + + keyToMetadata[key][chunkId] = bytes32(0x0); + + chunkId++; + } + + return chunkId; + } + + function _removeChunk( + bytes32 key, + uint256 chunkId + ) internal returns (bool) { + bytes32 metadata = keyToMetadata[key][chunkId]; + if (metadata == bytes32(0x0)) { + return false; + } + + if (keyToMetadata[key][chunkId + 1] != bytes32(0x0)) { + // only the last chunk can be removed + return false; + } + + if (!metadata.isInSlot()) { + address addr = metadata.bytes32ToAddr(); + // remove new contract + StorageSlotSelfDestructableV2(addr).destruct(); + } + + keyToMetadata[key][chunkId] = bytes32(0x0); + + return true; + } +} diff --git a/contracts/v3/database/EthStorage/LargerStorageManagerV2Test.sol b/contracts/v3/database/EthStorage/LargerStorageManagerV2Test.sol new file mode 100644 index 0000000..398de6b --- /dev/null +++ b/contracts/v3/database/EthStorage/LargerStorageManagerV2Test.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./LargeStorageManagerV2.sol"; + +contract LargeStorageManagerV2Test is LargeStorageManagerV2 { + function get(bytes32 key) public view returns (bytes memory, bool) { + (bytes memory data, bool found) = _get(key); + return (data, found); + } + + function getChunk( + bytes32 key, + uint256 chunkId + ) public view returns (bytes memory, bool) { + (bytes memory data, bool found) = _getChunk(key, chunkId); + return (data, found); + } + + function putChunk( + bytes32 key, + uint256 chunkId, + bytes memory data + ) public payable { + _putChunk(key, chunkId, data, msg.value); + } + + function putChunkFromCalldata( + bytes32 key, + uint256 chunkId, + bytes calldata data + ) public payable { + _putChunkFromCalldata(key, chunkId, data, msg.value); + } + + function size(bytes32 key) public view returns (uint256, uint256) { + return _size(key); + } + + function chunkSize( + bytes32 key, + uint256 chunkId + ) public view returns (uint256, bool) { + return _chunkSize(key, chunkId); + } + + function countChunks(bytes32 key) public view returns (uint256) { + return _countChunks(key); + } + + function remove(bytes32 key) public { + _remove(key, 0); + } + + function removeChunk(bytes32 key, uint256 chunkId) public { + _removeChunk(key, chunkId); + } + + function getBalance() public view returns (uint256 balance) { + return address(this).balance; + } + + function stakeTokens( + bytes32 key, + uint256 chunkId + ) public view returns (uint256) { + return _stakeTokens(key, chunkId); + } + + function chunkStakeTokens( + bytes32 key, + uint256 chunkId + ) public view returns (uint256, bool) { + return _chunkStakeTokens(key, chunkId); + } + + function getChunkAddr( + bytes32 key, + uint256 chunkId + ) public view returns (address) { + return _getChunkAddr(key, chunkId); + } +} diff --git a/contracts/v3/database/EthStorage/Memory.sol b/contracts/v3/database/EthStorage/Memory.sol new file mode 100644 index 0000000..ac6243b --- /dev/null +++ b/contracts/v3/database/EthStorage/Memory.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +library Memory { + // Size of a word, in bytes. + uint256 internal constant WORD_SIZE = 32; + // Size of the header of a 'bytes' array. + uint256 internal constant BYTES_HEADER_SIZE = 32; + // Address of the free memory pointer. + uint256 internal constant FREE_MEM_PTR = 0x40; + + // Compares the 'len' bytes starting at address 'addr' in memory with the 'len' + // bytes starting at 'addr2'. + // Returns 'true' if the bytes are the same, otherwise 'false'. + function equals( + uint256 addr, + uint256 addr2, + uint256 len + ) internal pure returns (bool equal) { + assembly { + equal := eq(keccak256(addr, len), keccak256(addr2, len)) + } + } + + // Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in + // 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only + // the first 'len' bytes will be compared. + // Requires that 'bts.length >= len' + function equals( + uint256 addr, + uint256 len, + bytes memory bts + ) internal pure returns (bool equal) { + require(bts.length >= len); + uint256 addr2; + assembly { + addr2 := add( + bts, + /*BYTES_HEADER_SIZE*/ + 32 + ) + } + return equals(addr, addr2, len); + } + + // Allocates 'numBytes' bytes in memory. This will prevent the Solidity compiler + // from using this area of memory. It will also initialize the area by setting + // each byte to '0'. + function allocate(uint256 numBytes) internal pure returns (uint256 addr) { + // Take the current value of the free memory pointer, and update. + assembly { + addr := mload( + /*FREE_MEM_PTR*/ + 0x40 + ) + mstore( + /*FREE_MEM_PTR*/ + 0x40, + add(addr, numBytes) + ) + } + uint256 words = (numBytes + WORD_SIZE - 1) / WORD_SIZE; + for (uint256 i = 0; i < words; i++) { + assembly { + mstore( + add( + addr, + mul( + i, + /*WORD_SIZE*/ + 32 + ) + ), + 0 + ) + } + } + } + + // Copy 'len' bytes from memory address 'src', to address 'dest'. + // This function does not check the or destination, it only copies + // the bytes. + function copy(uint256 src, uint256 dest, uint256 len) internal pure { + // Copy word-length chunks while possible + // Reverse copy to prevent out of memory bound error + src = src + len; + dest = dest + len; + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + dest -= WORD_SIZE; + src -= WORD_SIZE; + + assembly { + mstore(dest, mload(src)) + } + } + + if (len == 0) { + return; + } + + // Copy remaining bytes + src = src - len; + dest = dest - len; + assembly { + mstore(dest, mload(src)) + } + } + + // Returns a memory pointer to the provided bytes array. + function ptr(bytes memory bts) internal pure returns (uint256 addr) { + assembly { + addr := bts + } + } + + // Returns a memory pointer to the data portion of the provided bytes array. + function dataPtr(bytes memory bts) internal pure returns (uint256 addr) { + assembly { + addr := add( + bts, + /*BYTES_HEADER_SIZE*/ + 32 + ) + } + } + + // This function does the same as 'dataPtr(bytes memory)', but will also return the + // length of the provided bytes array. + function fromBytes( + bytes memory bts + ) internal pure returns (uint256 addr, uint256 len) { + len = bts.length; + assembly { + addr := add( + bts, + /*BYTES_HEADER_SIZE*/ + 32 + ) + } + } + + // Creates a 'bytes memory' variable from the memory address 'addr', with the + // length 'len'. The function will allocate new memory for the bytes array, and + // the 'len bytes starting at 'addr' will be copied into that new memory. + function toBytes( + uint256 addr, + uint256 len + ) internal pure returns (bytes memory bts) { + bts = new bytes(len); + uint256 btsptr; + assembly { + btsptr := add( + bts, + /*BYTES_HEADER_SIZE*/ + 32 + ) + } + copy(addr, btsptr, len); + } + + // Get the word stored at memory address 'addr' as a 'uint'. + function toUint(uint256 addr) internal pure returns (uint256 n) { + assembly { + n := mload(addr) + } + } + + // Get the word stored at memory address 'addr' as a 'bytes32'. + function toBytes32(uint256 addr) internal pure returns (bytes32 bts) { + assembly { + bts := mload(addr) + } + } + + /* + // Get the byte stored at memory address 'addr' as a 'byte'. + function toByte(uint addr, uint8 index) internal pure returns (byte b) { + require(index < WORD_SIZE); + uint8 n; + assembly { + n := byte(index, mload(addr)) + } + b = byte(n); + } + */ +} diff --git a/contracts/v3/database/EthStorage/StorageHelperV2.sol b/contracts/v3/database/EthStorage/StorageHelperV2.sol new file mode 100644 index 0000000..f638e7c --- /dev/null +++ b/contracts/v3/database/EthStorage/StorageHelperV2.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Memory.sol"; +import "./StorageSlotFactory.sol"; + +library StorageHelperV2 { + // StorageSlotSelfDestructableV2 compiled via solc 0.8.7 optimized 200 + bytes internal constant STORAGE_SLOT_CODE_V2 = + hex"6080604052348015600f57600080fd5b506004361060285760003560e01c80632b68b9c614602d575b600080fd5b60336035565b005b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161460965760405162461bcd60e51b81526020600482015260036024820152624e464f60e81b604482015260640160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316fffea2646970667358221220154417754813d1989858c876ab2ded2ba1aa380679fff7a4c8faea076ba020e664736f6c63430008070033"; + uint256 internal constant OWNER_ADDR_OFF = 64; + uint256 internal constant USER_ADDR_OFF = 152; + + // StorageSlotFactoryFromInput compiled via solc 0.8.7 optimized 200 + STORAGE_SLOT_CODE + bytes internal constant FACTORY_CODE = + hex"60806040526040516101113803806101118339810160408190526100229161002b565b80518060208301f35b6000602080838503121561003e57600080fd5b82516001600160401b038082111561005557600080fd5b818501915085601f83011261006957600080fd5b81518181111561007b5761007b6100fa565b604051601f8201601f19908116603f011681019083821181831017156100a3576100a36100fa565b8160405282815288868487010111156100bb57600080fd5b600093505b828410156100dd57848401860151818501870152928501926100c0565b828411156100ee5760008684830101525b98975050505050505050565b634e487b7160e01b600052604160045260246000fdfe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000006080604052348015600f57600080fd5b506004361060325760003560e01c80632b68b9c61460375780638da5cb5b14603f575b600080fd5b603d6081565b005b60657f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161460ed5760405162461bcd60e51b815260206004820152600e60248201526d3737ba10333937b69037bbb732b960911b604482015260640160405180910390fd5b33fffea2646970667358221220fc66c9afb7cb2f6209ae28167cf26c6c06f86a82cbe3c56de99027979389a1be64736f6c63430008070033"; + uint256 internal constant FACTORY_SIZE_OFF = 305; + uint256 internal constant FACTORY_ADDR_OFF0 = 305 + 32 + OWNER_ADDR_OFF; + uint256 internal constant FACTORY_ADDR_OFF1 = 305 + 32 + USER_ADDR_OFF; + + function putRawFromCalldata( + bytes calldata data, + uint256 value + ) internal returns (address) { + bytes memory bytecode = bytes.concat(STORAGE_SLOT_CODE_V2, data); + address userAddr = msg.sender; + { + // revise the owner to the contract (so that it is destructable) + uint256 off = OWNER_ADDR_OFF + 0x20; + assembly { + mstore(add(bytecode, off), address()) + } + off = USER_ADDR_OFF + 0x20; + assembly { + mstore(add(bytecode, off), userAddr) + } + } + + StorageSlotFactoryFromInput c = new StorageSlotFactoryFromInput{ + value: value + }(bytecode); + return address(c); + } + + function putRaw( + bytes memory data, + uint256 value + ) internal returns (address) { + // create the new contract code with the data + bytes memory bytecode = STORAGE_SLOT_CODE_V2; + uint256 bytecodeLen = bytecode.length; + uint256 newSize = bytecode.length + data.length; + assembly { + // in-place resize of bytecode bytes + // note that this must be done when bytecode is the last allocated object by solidity. + mstore(bytecode, newSize) + // notify solidity about the memory size increase, must be 32-bytes aligned + mstore( + 0x40, + add(bytecode, and(add(add(newSize, 0x20), 0x1f), not(0x1f))) + ) + } + // append data to self-destruct byte code + Memory.copy( + Memory.dataPtr(data), + Memory.dataPtr(bytecode) + bytecodeLen, + data.length + ); + address userAddr = msg.sender; + { + // revise the owner to the contract (so that it is destructable) + uint256 off = OWNER_ADDR_OFF + 0x20; + assembly { + mstore(add(bytecode, off), address()) + } + off = USER_ADDR_OFF + 0x20; + assembly { + mstore(add(bytecode, off), userAddr) + } + } + + StorageSlotFactoryFromInput c = new StorageSlotFactoryFromInput{ + value: value + }(bytecode); + return address(c); + } + + function putRaw2( + bytes32 key, + bytes memory data, + uint256 value + ) internal returns (address) { + // create the new contract code with the data + bytes memory bytecode = FACTORY_CODE; + uint256 bytecodeLen = bytecode.length; + uint256 newSize = bytecode.length + data.length; + assembly { + // in-place resize of bytecode bytes + // note that this must be done when bytecode is the last allocated object by solidity. + mstore(bytecode, newSize) + // notify solidity about the memory size increase, must be 32-bytes aligned + mstore( + 0x40, + add(bytecode, and(add(add(newSize, 0x20), 0x1f), not(0x1f))) + ) + } + // append data to self-destruct byte code + Memory.copy( + Memory.dataPtr(data), + Memory.dataPtr(bytecode) + bytecodeLen, + data.length + ); + { + // revise the size of calldata + uint256 calldataSize = STORAGE_SLOT_CODE_V2.length + data.length; + uint256 off = FACTORY_SIZE_OFF + 0x20; + assembly { + mstore(add(bytecode, off), calldataSize) + } + } + { + // revise the owner to the contract (so that it is destructable) + uint256 off = FACTORY_ADDR_OFF0 + 0x20; + assembly { + mstore(add(bytecode, off), address()) + } + off = FACTORY_ADDR_OFF1 + 0x20; + assembly { + mstore(add(bytecode, off), address()) + } + } + + address addr; + assembly { + addr := create2( + value, + add(bytecode, 0x20), // data offset + mload(bytecode), // size + key + ) + + if iszero(extcodesize(addr)) { + revert(0, 0) + } + } + return addr; + } + + function sizeRaw(address addr) internal view returns (uint256, bool) { + if (addr == address(0x0)) { + return (0, false); + } + uint256 codeSize; + uint256 off = STORAGE_SLOT_CODE_V2.length; + assembly { + codeSize := extcodesize(addr) + } + if (codeSize < off) { + return (0, false); + } + + return (codeSize - off, true); + } + + function getRaw(address addr) internal view returns (bytes memory, bool) { + (uint256 dataSize, bool found) = sizeRaw(addr); + + if (!found) { + return (new bytes(0), false); + } + + // copy the data without the "code" + bytes memory data = new bytes(dataSize); + uint256 off = STORAGE_SLOT_CODE_V2.length; + assembly { + // retrieve data size + extcodecopy(addr, add(data, 0x20), off, dataSize) + } + return (data, true); + } + + function getRawAt( + address addr, + uint256 memoryPtr + ) internal view returns (uint256, bool) { + (uint256 dataSize, bool found) = sizeRaw(addr); + + if (!found) { + return (0, false); + } + + uint256 off = STORAGE_SLOT_CODE_V2.length; + assembly { + // retrieve data size + extcodecopy(addr, memoryPtr, off, dataSize) + } + return (dataSize, true); + } + + function returnBytesInplace(bytes memory content) internal pure { + // equal to return abi.encode(content) + uint256 size = content.length + 0x40; // pointer + size + size = (size + 0x20 + 0x1f) & ~uint256(0x1f); + assembly { + // (DATA CORRUPTION): the caller method must be "external returns (bytes)", cannot be public! + mstore(sub(content, 0x20), 0x20) + return(sub(content, 0x20), size) + } + } + + function calculateValueForData( + uint256 datalen, + uint256 chunkSize, + uint256 codeStakingPerChunk + ) internal pure returns (uint256) { + return + ((datalen + STORAGE_SLOT_CODE_V2.length - 1) / chunkSize) * + codeStakingPerChunk; + } + + function storageSlotCodeLength() internal pure returns (uint256) { + return STORAGE_SLOT_CODE_V2.length; + } +} diff --git a/contracts/v3/database/EthStorage/StorageSlotFactory.sol b/contracts/v3/database/EthStorage/StorageSlotFactory.sol new file mode 100644 index 0000000..b9e88be --- /dev/null +++ b/contracts/v3/database/EthStorage/StorageSlotFactory.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Memory.sol"; + +// Create a storage slot by appending data to the end +contract StorageSlotFromContract { + constructor(address contractAddr, bytes memory data) payable { + uint256 codeSize; + assembly { + // retrieve the size of the code, this needs assembly + codeSize := extcodesize(contractAddr) + } + + uint256 totalSize = codeSize + data.length + 32; + bytes memory deployCode = new bytes(totalSize); + + // Copy contract code + assembly { + // actually retrieve the code, this needs assembly + extcodecopy(contractAddr, add(deployCode, 0x20), 0, codeSize) + } + + // Copy data + uint256 off = Memory.dataPtr(deployCode) + codeSize; + Memory.copy(Memory.dataPtr(data), off, data.length); + + off += data.length; + uint256 len = data.length; + // Set data size + assembly { + mstore(off, len) + } + + // Return the contract manually + assembly { + return(add(deployCode, 0x20), totalSize) + } + } +} + +// Create a storage slot +contract StorageSlotFactoryFromInput { + constructor(bytes memory codeAndData) payable { + uint256 size = codeAndData.length; + // Return the contract manually + assembly { + return(add(codeAndData, 0x20), size) + } + } +} diff --git a/contracts/v3/database/EthStorage/StorageSlotSelfDestructableV2.sol b/contracts/v3/database/EthStorage/StorageSlotSelfDestructableV2.sol new file mode 100644 index 0000000..ae6c5d3 --- /dev/null +++ b/contracts/v3/database/EthStorage/StorageSlotSelfDestructableV2.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract StorageSlotSelfDestructableV2 { + address immutable owner; + address immutable userToRefund; + + constructor(address user) payable { + owner = msg.sender; + userToRefund = user; + } + + function destruct() public { + require(msg.sender == owner, "NFO"); + selfdestruct(payable(userToRefund)); + } +} + +contract StorageSlotSelfDestructableV2_DEBUG { + address public immutable owner; + address public immutable userToRefund; + + constructor(address user) payable { + owner = msg.sender; + userToRefund = user; + } + + function destruct() public { + require(msg.sender == owner, "NFO"); + selfdestruct(payable(userToRefund)); + } +} diff --git a/contracts/v3/database/EthStorage/optimize/SlotHelper.sol b/contracts/v3/database/EthStorage/optimize/SlotHelper.sol new file mode 100644 index 0000000..8abb86a --- /dev/null +++ b/contracts/v3/database/EthStorage/optimize/SlotHelper.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library SlotHelper { + uint256 internal constant SLOTDATA_RIGHT_SHIFT = 32; + uint256 internal constant LEN_OFFSET = 224; + uint256 internal constant FIRST_SLOT_DATA_SIZE = 28; + + function putRaw( + mapping(uint256 => bytes32) storage slots, + bytes memory datas + ) internal returns (bytes32 mdata) { + uint256 len = datas.length; + mdata = encodeMetadata(datas); + if (len > FIRST_SLOT_DATA_SIZE) { + bytes32 value; + uint256 ptr; + assembly { + ptr := add(datas, add(0x20, FIRST_SLOT_DATA_SIZE)) + } + for ( + uint256 i = 0; + i < (len - FIRST_SLOT_DATA_SIZE + 32 - 1) / 32; + i++ + ) { + assembly { + value := mload(ptr) + } + ptr = ptr + 32; + slots[i] = value; + } + } + } + + function encodeMetadata( + bytes memory data + ) internal pure returns (bytes32 medata) { + uint256 datLen = data.length; + uint256 value; + assembly { + value := mload(add(data, 0x20)) + } + + datLen = datLen << LEN_OFFSET; + value = value >> SLOTDATA_RIGHT_SHIFT; + + medata = bytes32(value | datLen); + } + + function decodeMetadata( + bytes32 mdata + ) internal pure returns (uint256 len, bytes32 data) { + len = decodeLen(mdata); + data = mdata << SLOTDATA_RIGHT_SHIFT; + } + + function decodeMetadataToData( + bytes32 mdata + ) internal pure returns (uint256 len, bytes memory data) { + len = decodeLen(mdata); + mdata = mdata << SLOTDATA_RIGHT_SHIFT; + data = new bytes(len); + assembly { + mstore(add(data, 0x20), mdata) + } + } + + function getRaw( + mapping(uint256 => bytes32) storage slots, + bytes32 mdata + ) internal view returns (bytes memory data) { + uint256 datalen; + (datalen, data) = decodeMetadataToData(mdata); + + if (datalen > FIRST_SLOT_DATA_SIZE) { + uint256 ptr = 0; + bytes32 value = 0; + assembly { + ptr := add(data, add(0x20, FIRST_SLOT_DATA_SIZE)) + } + for ( + uint256 i = 0; + i < (datalen - FIRST_SLOT_DATA_SIZE + 32 - 1) / 32; + i++ + ) { + value = slots[i]; + assembly { + mstore(ptr, value) + } + ptr = ptr + 32; + } + } + } + + function getRawAt( + mapping(uint256 => bytes32) storage slots, + bytes32 mdata, + uint256 memoryPtr + ) internal view returns (uint256 datalen, bool found) { + bytes32 datapart; + (datalen, datapart) = decodeMetadata(mdata); + + // memoryPtr:memoryPtr+32 is allocated for the data + uint256 dataPtr = memoryPtr; + assembly { + mstore(dataPtr, datapart) + } + + if (datalen > FIRST_SLOT_DATA_SIZE) { + uint256 ptr = 0; + bytes32 value = 0; + + assembly { + ptr := add(dataPtr, FIRST_SLOT_DATA_SIZE) + } + for ( + uint256 i = 0; + i < (datalen - FIRST_SLOT_DATA_SIZE + 32 - 1) / 32; + i++ + ) { + value = slots[i]; + assembly { + mstore(ptr, value) + } + ptr = ptr + 32; + } + } + + found = true; + } + + function isInSlot(bytes32 mdata) internal pure returns (bool succeed) { + return decodeLen(mdata) > 0; + } + + function encodeLen(uint256 datalen) internal pure returns (bytes32 res) { + res = bytes32(datalen << LEN_OFFSET); + } + + function decodeLen(bytes32 mdata) internal pure returns (uint256 res) { + res = uint256(mdata) >> LEN_OFFSET; + } + + function addrToBytes32(address addr) internal pure returns (bytes32) { + return bytes32(uint256(uint160(addr))); + } + + function bytes32ToAddr(bytes32 bt) internal pure returns (address) { + return address(uint160(uint256(bt))); + } +} diff --git a/contracts/v3/database/EthStorage/optimize/SlotHelperTest.sol b/contracts/v3/database/EthStorage/optimize/SlotHelperTest.sol new file mode 100644 index 0000000..f630bd0 --- /dev/null +++ b/contracts/v3/database/EthStorage/optimize/SlotHelperTest.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./SlotHelper.sol"; + +contract SlotHelperTest { + mapping(bytes32 => bytes32) public metadatas; + mapping(bytes32 => mapping(uint256 => bytes32)) public slots; + + function put(bytes32 key, bytes memory data) public { + metadatas[key] = SlotHelper.putRaw(slots[key], data); + } + + function get(bytes32 key) public view returns (bytes memory res) { + bytes32 md = metadatas[key]; + res = SlotHelper.getRaw(slots[key], md); + } + + function encodeMetadata(bytes memory data) public pure returns (bytes32) { + return SlotHelper.encodeMetadata(data); + } + + function decodeMetadata( + bytes32 mdata + ) public pure returns (uint256, bytes32) { + return SlotHelper.decodeMetadata(mdata); + } + + function decodeMetadata1( + bytes32 mdata + ) public pure returns (uint256, bytes memory) { + return SlotHelper.decodeMetadataToData(mdata); + } + + function encodeLen(uint256 datalen) public pure returns (bytes32) { + return SlotHelper.encodeLen(datalen); + } + + function decodeLen(bytes32 mdata) public pure returns (uint256 res) { + res = SlotHelper.decodeLen(mdata); + } + + function getLen(bytes32 key) public view returns (uint256 resLen) { + bytes32 mdata = metadatas[key]; + resLen = SlotHelper.decodeLen(mdata); + } +} diff --git a/contracts/v3/database/database.sol b/contracts/v3/database/database.sol new file mode 100644 index 0000000..497d3b0 --- /dev/null +++ b/contracts/v3/database/database.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.0; + +interface database { + + function download( + bytes memory repoName, + bytes memory path + ) external view returns (bytes memory, bool) ; + + function upload( + bytes memory repoName, + bytes memory path, + bytes calldata data + ) external payable; + +} \ No newline at end of file diff --git a/contracts/v3/database/ethstorage.sol b/contracts/v3/database/ethstorage.sol new file mode 100644 index 0000000..a1ecea4 --- /dev/null +++ b/contracts/v3/database/ethstorage.sol @@ -0,0 +1,95 @@ +pragma solidity ^0.8.0; +import "./EthStorage/LargeStorageManagerV2.sol"; +contract EthStorage is LargeStorageManagerV2{ + function stakeTokens( + bytes memory repoName, + bytes memory path + ) external view returns (uint256) { + bytes memory fullPath = bytes.concat(repoName, "/", path); + return _stakeTokens(keccak256(fullPath), 0); + } + + function chunkStakeTokens( + bytes memory repoName, + bytes memory path, + uint256 chunkId + ) external view returns (uint256) { + bytes memory fullPath = bytes.concat(repoName, "/", path); + (uint256 sTokens, ) = _chunkStakeTokens(keccak256(fullPath), chunkId); + return sTokens; + } + + function getChunkAddr( + bytes memory repoName, + bytes memory path, + uint256 chunkId + ) external view returns (address) { + bytes memory fullPath = bytes.concat(repoName, "/", path); + return _getChunkAddr(keccak256(fullPath), chunkId); + } + + function download( + bytes memory repoName, + bytes memory path + ) external view returns (bytes memory, bool) { + // call flat directory(FD) + return _get(keccak256(bytes.concat(repoName, "/", path))); + } + + function upload( + bytes memory repoName, + bytes memory path, + bytes calldata data + ) external payable { + _putChunkFromCalldata( + keccak256(bytes.concat(repoName, "/", path)), + 0, + data, + msg.value + ); + } + + function uploadChunk( + bytes memory repoName, + bytes memory path, + uint256 chunkId, + bytes calldata data + ) external payable{ + _putChunkFromCalldata( + keccak256(bytes.concat(repoName, "/", path)), + chunkId, + data, + msg.value + ); + } + + function remove( + bytes memory repoName, + bytes memory path + ) external { + // The actually process of remove will remove all the chunks + _remove(keccak256(bytes.concat(repoName, "/", path)), 0); + } + + function removeChunk( + bytes memory repoName, + bytes memory path, + uint256 chunkId + ) external { + _removeChunk(keccak256(bytes.concat(repoName, "/", path)), chunkId); + } + + function size( + bytes memory repoName, + bytes memory name + ) external view returns (uint256, uint256) { + return _size(keccak256(bytes.concat(repoName, "/", name))); + } + + function countChunks( + bytes memory repoName, + bytes memory name + ) external view returns (uint256) { + return _countChunks(keccak256(bytes.concat(repoName, "/", name))); + } +} \ No newline at end of file diff --git a/contracts/v3/database/filecoin.sol b/contracts/v3/database/filecoin.sol new file mode 100644 index 0000000..74d1608 --- /dev/null +++ b/contracts/v3/database/filecoin.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.8.0; + +contract filecoin{ + + mapping(bytes32 => bytes) public pathToHash; + + function download( + bytes memory repoName, + bytes memory path + ) external view returns (bytes memory, bool) { + + bytes32 fullName = keccak256(bytes.concat(repoName, "/", path)); + // call flat directory(FD) + return (pathToHash[fullName],true); + } + + function upload( + bytes memory repoName, + bytes memory path, + bytes calldata data + ) external payable { + bytes32 fullName = keccak256(bytes.concat(repoName, "/", path)); + pathToHash[fullName] = data; + + } + +} \ No newline at end of file diff --git a/contracts/v3/storage/IStorageLayer.sol b/contracts/v3/storage/IStorageLayer.sol deleted file mode 100644 index 7ff417a..0000000 --- a/contracts/v3/storage/IStorageLayer.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.8.0; - - -interface IStorageLayer{ - - function upload( - bytes20 refHash, - bytes calldata data - ) external payable ; - - function download(bytes20 refHash) external view returns(bytes32 storageLayerId,bytes memory data); -} \ No newline at end of file