Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt to the latest blob rules #5

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions contracts/ERC5018ForBlob.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,32 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./IERC5018ForBlob.sol";

enum DecodeType {
RawData,
PaddingPer31Bytes
}

interface EthStorageContract {
function putBlob(bytes32 key, uint256 blobIdx, uint256 length) external payable;

function get(bytes32 key, uint256 off, uint256 len) external view returns (bytes memory);
function get(bytes32 key, DecodeType decodeType, uint256 off, uint256 len) external view returns (bytes memory);

function remove(bytes32 key) external;

function size(bytes32 key) external view returns (uint256);

function hash(bytes32 key) external view returns (bytes24);

function upfrontPayment() external view returns (uint256);
}

contract ERC5018ForBlob is IERC5018ForBlob, Ownable {

uint32 BLOB_SIZE = 4096 * 32;
uint32 DECODE_BLOB_SIZE = 4096 * 31;

EthStorageContract public storageContract;

mapping(bytes32 => bytes32[]) internal keyToChunk;
mapping(bytes32 => uint256) internal chunkSizes;

function setEthStorageContract(address storageAddress) public onlyOwner {
storageContract = EthStorageContract(storageAddress);
Expand All @@ -36,8 +43,8 @@ contract ERC5018ForBlob is IERC5018ForBlob, Ownable {
if (chunkId >= _countChunks(key)) {
return (0, false);
}
uint256 size_ = storageContract.size(keyToChunk[key][chunkId]);
return (size_, true);
bytes32 chunkKey = keyToChunk[key][chunkId];
return (chunkSizes[chunkKey], true);
}

function _size(bytes32 key) internal view returns (uint256, uint256) {
Expand All @@ -61,26 +68,30 @@ contract ERC5018ForBlob is IERC5018ForBlob, Ownable {
return (new bytes(0), false);
}

bytes memory data = storageContract.get(keyToChunk[key][chunkId], 0, length);
bytes memory data = storageContract.get(keyToChunk[key][chunkId], DecodeType.PaddingPer31Bytes, 0, length);
return (data, true);
}

function _get(bytes32 key) internal view returns (bytes memory, bool) {
(, uint256 chunkNum) = _size(key);
(uint256 fileSize, uint256 chunkNum) = _size(key);
if (chunkNum == 0) {
return (new bytes(0), false);
}

bytes memory data = new bytes(0);
bytes memory concatenatedData = new bytes(fileSize);
uint256 offset = 0;
for (uint256 chunkId = 0; chunkId < chunkNum; chunkId++) {
(bytes memory temp, bool state) = _getChunk(key, chunkId);
if (!state) {
break;
bytes32 chunkKey = keyToChunk[key][chunkId];
uint256 length = chunkSizes[chunkKey];
storageContract.get(chunkKey, DecodeType.PaddingPer31Bytes, 0, length);

assembly {
returndatacopy(add(add(concatenatedData, offset), 0x20), 0x40, length)
}
data = bytes.concat(data, temp);
offset += length;
}

return (data, true);
return (concatenatedData, true);
}

function _removeChunk(bytes32 key, uint256 chunkId) internal returns (bool) {
Expand Down Expand Up @@ -123,18 +134,19 @@ contract ERC5018ForBlob is IERC5018ForBlob, Ownable {
require(msg.value >= cost * length, "insufficient balance");

for (uint8 i = 0; i < length; i++) {
require(sizes[i] <= 4096 * 31, "invalid blob length");
require(sizes[i] <= DECODE_BLOB_SIZE, "invalid chunk length");
_preparePut(key, chunkIds[i]);

bytes32 chunkKey = keccak256(abi.encode(msg.sender, block.timestamp, chunkIds[i], i));
storageContract.putBlob{value : cost}(chunkKey, i, sizes[i]);
storageContract.putBlob{value : cost}(chunkKey, i, BLOB_SIZE);
iteyelmp marked this conversation as resolved.
Show resolved Hide resolved
if (chunkIds[i] < _countChunks(key)) {
// replace
keyToChunk[key][chunkIds[i]] = chunkKey;
} else {
// add
keyToChunk[key].push(chunkKey);
}
chunkSizes[chunkKey] = sizes[i];
}
}

Expand Down Expand Up @@ -195,7 +207,7 @@ contract ERC5018ForBlob is IERC5018ForBlob, Ownable {
refund();
}

function upfrontPayment() external override view returns (uint256) {
function upfrontPayment() external view returns (uint256) {
return storageContract.upfrontPayment();
}
}
2 changes: 0 additions & 2 deletions contracts/IERC5018ForBlob.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,4 @@ interface IERC5018ForBlob {
function getChunkHash(bytes memory name, uint256 chunkId) external view returns (bytes32);

function writeChunk(bytes memory name, uint256[] memory chunkIds, uint256[] memory sizes) external payable;

function upfrontPayment() external view returns (uint256);
}
47 changes: 47 additions & 0 deletions contracts/examples/FlatDirectoryForBlob.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC5018ForBlob.sol";

contract FlatDirectoryForBlob is ERC5018ForBlob {
bytes public defaultFile = "";

function resolveMode() external pure virtual returns (bytes32) {
return "manual";
}

fallback(bytes calldata pathinfo) external returns (bytes memory) {
bytes memory content;
if (pathinfo.length == 0) {
// TODO: redirect to "/"?
return bytes("");
} else if (pathinfo[0] != 0x2f) {
// Should not happen since manual mode will have prefix "/" like "/....."
return bytes("incorrect path");
}

if (pathinfo[pathinfo.length - 1] == 0x2f) {
(content,) = read(bytes.concat(pathinfo[1 :], defaultFile));
} else {
(content,) = read(pathinfo[1 :]);
}

returnBytesInplace(content);
}

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 setDefault(bytes memory _defaultFile) public onlyOwner virtual {
defaultFile = _defaultFile;
}
}