diff --git a/contracts/BlobStorageManager.sol b/contracts/BlobStorageManager.sol index cd2db25..10168f9 100644 --- a/contracts/BlobStorageManager.sol +++ b/contracts/BlobStorageManager.sol @@ -52,11 +52,7 @@ contract BlobStorageManager is Ownable { function _countChunksFromBlob(bytes32 key) internal view returns (uint256) { uint256 chunkId = 0; - while (true) { - bytes32 chunkKey = keyToChunks[key][chunkId]; - if (chunkKey == bytes32(0)) { - break; - } + while (keyToChunks[key][chunkId] != bytes32(0)) { chunkId++; } return chunkId; @@ -112,8 +108,7 @@ contract BlobStorageManager is Ownable { } function _removeChunkFromBlob(bytes32 key, uint256 chunkId) internal returns (bool) { - bytes32 chunkKey = keyToChunks[key][chunkId]; - if (chunkKey == bytes32(0)) { + if (keyToChunks[key][chunkId] == bytes32(0)) { return false; } if (keyToChunks[key][chunkId + 1] != bytes32(0)) { @@ -128,12 +123,7 @@ contract BlobStorageManager is Ownable { } function _removeFromBlob(bytes32 key, uint256 chunkId) internal returns (uint256) { - while (true) { - bytes32 chunkKey = keyToChunks[key][chunkId]; - if (chunkKey == bytes32(0)) { - break; - } - + while (keyToChunks[key][chunkId] != bytes32(0)) { // TODO The current version does not support the delete // storageContract.remove(keyToChunks[key][chunkId]); keyToChunks[key][chunkId] = bytes32(0); @@ -143,8 +133,7 @@ contract BlobStorageManager is Ownable { } function _preparePutFromBlob(bytes32 key, uint256 chunkId) private { - bytes32 chunkKey = keyToChunks[key][chunkId]; - if (chunkKey == bytes32(0)) { + if (keyToChunks[key][chunkId] == bytes32(0)) { require(chunkId == 0 || keyToChunks[key][chunkId - 1] != bytes32(0), "must replace or append"); } else { // TODO The current version does not support the delete diff --git a/contracts/ERC5018.sol b/contracts/ERC5018.sol index 881d4f6..f015107 100644 --- a/contracts/ERC5018.sol +++ b/contracts/ERC5018.sol @@ -8,11 +8,6 @@ import "./ISemver.sol"; contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { - enum StorageMode { - Uninitialized, - OnChain, - Blob - } mapping(bytes32 => StorageMode) storageModes; constructor( @@ -31,10 +26,6 @@ contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { return storageModes[keccak256(name)]; } - function _setStorageMode(bytes memory name, StorageMode mode) internal { - storageModes[keccak256(name)] = mode; - } - // Large storage methods function write(bytes memory name, bytes calldata data) public onlyOwner payable virtual override { // TODO: support multiple chunks @@ -42,41 +33,42 @@ contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { } function read(bytes memory name) public view virtual override returns (bytes memory, bool) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _getFromBlob(keccak256(name)); + return _getFromBlob(key); } else if (mode == StorageMode.OnChain) { - return _get(keccak256(name)); + return _get(key); } return (new bytes(0), false); } function size(bytes memory name) public view virtual override returns (uint256, uint256) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _sizeFromBlob(keccak256(name)); + return _sizeFromBlob(key); } else if (mode == StorageMode.OnChain) { - return _size(keccak256(name)); + return _size(key); } return (0, 0); } function remove(bytes memory name) public virtual override onlyOwner returns (uint256) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); + storageModes[key] = StorageMode.Uninitialized; if (mode == StorageMode.Blob) { - return _removeFromBlob(keccak256(name), 0); + return _removeFromBlob(key, 0); } else if (mode == StorageMode.OnChain) { - return _remove(keccak256(name), 0); + return _remove(key, 0); } return 0; } function countChunks(bytes memory name) public view virtual override returns (uint256) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _countChunksFromBlob(keccak256(name)); + return _countChunksFromBlob(key); } else if (mode == StorageMode.OnChain) { - return _countChunks(keccak256(name)); + return _countChunks(key); } return 0; } @@ -87,12 +79,12 @@ contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { uint256 chunkId, bytes calldata data ) public payable onlyOwner virtual override { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); require(mode == StorageMode.Uninitialized || mode == StorageMode.OnChain, "Invalid storage mode"); if (mode == StorageMode.Uninitialized) { - _setStorageMode(name, StorageMode.OnChain); + storageModes[key] = StorageMode.OnChain; } - _putChunkFromCalldata(keccak256(name), chunkId, data, msg.value); + _putChunkFromCalldata(key, chunkId, data, msg.value); } function writeChunks( @@ -102,50 +94,50 @@ contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { ) public onlyOwner override payable { require(isSupportBlob(), "The current network does not support blob upload"); - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); require(mode == StorageMode.Uninitialized || mode == StorageMode.Blob, "Invalid storage mode"); if (mode == StorageMode.Uninitialized) { - _setStorageMode(name, StorageMode.Blob); + storageModes[key] = StorageMode.Blob; } - _putChunks(keccak256(name), chunkIds, sizes); + _putChunks(key, chunkIds, sizes); } function readChunk(bytes memory name, uint256 chunkId) public view virtual override returns (bytes memory, bool) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _getChunkFromBlob(keccak256(name), chunkId); + return _getChunkFromBlob(key, chunkId); } else if (mode == StorageMode.OnChain) { - return _getChunk(keccak256(name), chunkId); + return _getChunk(key, chunkId); } return (new bytes(0), false); } function chunkSize(bytes memory name, uint256 chunkId) public view virtual override returns (uint256, bool) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _chunkSizeFromBlob(keccak256(name), chunkId); + return _chunkSizeFromBlob(key, chunkId); } else if (mode == StorageMode.OnChain) { - return _chunkSize(keccak256(name), chunkId); + return _chunkSize(key, chunkId); } return (0, false); } function removeChunk(bytes memory name, uint256 chunkId) public virtual onlyOwner override returns (bool) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _removeChunkFromBlob(keccak256(name), chunkId); + return _removeChunkFromBlob(key, chunkId); } else if (mode == StorageMode.OnChain) { - return _removeChunk(keccak256(name), chunkId); + return _removeChunk(key, chunkId); } return false; } function truncate(bytes memory name, uint256 chunkId) public virtual onlyOwner override returns (uint256) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _removeFromBlob(keccak256(name), chunkId); + return _removeFromBlob(key, chunkId); } else if (mode == StorageMode.OnChain) { - return _remove(keccak256(name), chunkId); + return _remove(key, chunkId); } return 0; } @@ -159,13 +151,59 @@ contract ERC5018 is LargeStorageManager, BlobStorageManager, IERC5018, ISemver { } function getChunkHash(bytes memory name, uint256 chunkId) public override view returns (bytes32) { - StorageMode mode = getStorageMode(name); + (bytes32 key, StorageMode mode) = _getModeAndKey(name); if (mode == StorageMode.Blob) { - return _getChunkHashFromBlob(keccak256(name), chunkId); + return _getChunkHashFromBlob(key, chunkId); } else if (mode == StorageMode.OnChain) { - (bytes memory localData,) = readChunk(name, chunkId); + (bytes memory localData,) = _getChunk(key, chunkId); return keccak256(localData); } return 0; } + + function getChunkHashesBatch(FileChunk[] memory fileChunks) external view returns (bytes32[] memory) { + uint totalChunks = 0; + + for (uint i = 0; i < fileChunks.length; i++) { + totalChunks += fileChunks[i].chunkIds.length; + } + + bytes32[] memory hashes = new bytes32[](totalChunks); + uint index = 0; + for (uint i = 0; i < fileChunks.length; i++) { + for (uint j = 0; j < fileChunks[i].chunkIds.length; j++) { + hashes[index] = getChunkHash(fileChunks[i].name, fileChunks[i].chunkIds[j]); + index++; + } + } + return hashes; + } + + function getChunkCountsBatch(bytes[] memory names) external view returns (uint256[] memory) { + uint256[] memory counts = new uint256[](names.length); + for (uint i = 0; i < names.length; i++) { + counts[i] = countChunks(names[i]); + } + return counts; + } + + function getUploadInfo(bytes memory name) public override view returns (StorageMode mode, uint256 chunkCount, uint256 storageCost) { + bytes32 key; + (key, mode) = _getModeAndKey(name); + + if (mode == StorageMode.Blob) { + chunkCount = _countChunksFromBlob(key); + } else if (mode == StorageMode.OnChain) { + chunkCount = _countChunks(key); + } else { + chunkCount = 0; + } + + storageCost = address(storageContract) != address(0) ? upfrontPayment() : 0; + } + + function _getModeAndKey(bytes memory name) private view returns (bytes32 key, StorageMode mode) { + key = keccak256(name); + mode = storageModes[key]; + } } diff --git a/contracts/IERC5018.sol b/contracts/IERC5018.sol index 839af35..c1d87c1 100644 --- a/contracts/IERC5018.sol +++ b/contracts/IERC5018.sol @@ -2,6 +2,17 @@ pragma solidity ^0.8.0; interface IERC5018 { + enum StorageMode { + Uninitialized, + OnChain, + Blob + } + + struct FileChunk { + bytes name; + uint256[] chunkIds; + } + // Large storage methods function write(bytes memory name, bytes memory data) external payable; @@ -36,4 +47,10 @@ interface IERC5018 { function destruct() external; function getChunkHash(bytes memory name, uint256 chunkId) external view returns (bytes32); + + function getChunkHashesBatch(FileChunk[] memory fileChunks) external view returns (bytes32[] memory); + + function getChunkCountsBatch(bytes[] memory names) external view returns (uint256[] memory); + + function getUploadInfo(bytes memory name) external view returns (StorageMode mode, uint256 chunkCount, uint256 storageCost); } diff --git a/contracts/LargeStorageManager.sol b/contracts/LargeStorageManager.sol index dc07578..e1207b8 100644 --- a/contracts/LargeStorageManager.sol +++ b/contracts/LargeStorageManager.sol @@ -75,11 +75,9 @@ contract LargeStorageManager { bytes32 metadata = keyToMetadata[key][chunkId]; if (metadata.isInSlot()) { - bytes memory res = SlotHelper.getRaw(keyToSlots[key][chunkId], metadata); - return (res, true); + return (SlotHelper.getRaw(keyToSlots[key][chunkId], metadata), true); } else { - address addr = metadata.bytes32ToAddr(); - return StorageHelper.getRaw(addr); + return StorageHelper.getRaw(metadata.bytes32ToAddr()); } } @@ -89,23 +87,16 @@ contract LargeStorageManager { if (metadata == bytes32(0)) { return (0, false); } else if (metadata.isInSlot()) { - uint256 len = metadata.decodeLen(); - return (len, true); + return (metadata.decodeLen(), true); } else { - address addr = metadata.bytes32ToAddr(); - return StorageHelper.sizeRaw(addr); + return StorageHelper.sizeRaw(metadata.bytes32ToAddr()); } } function _countChunks(bytes32 key) internal view returns (uint256) { uint256 chunkId = 0; - while (true) { - bytes32 metadata = keyToMetadata[key][chunkId]; - if (metadata == bytes32(0x0)) { - break; - } - + while (keyToMetadata[key][chunkId] != bytes32(0)) { chunkId++; } @@ -162,11 +153,8 @@ contract LargeStorageManager { // Returns # of chunks deleted function _remove(bytes32 key, uint256 chunkId) internal returns (uint256) { - while (true) { + while (keyToMetadata[key][chunkId] != bytes32(0)) { bytes32 metadata = keyToMetadata[key][chunkId]; - if (metadata == bytes32(0x0)) { - break; - } if (!metadata.isInSlot()) { address addr = metadata.bytes32ToAddr();