You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.8 KiB
Solidity

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
import "./IFileOperator.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "evm-large-storage/contracts/LargeStorageManager.sol";
// import "evm-large-storage/contracts/W3RC3.sol";
contract Git3 is LargeStorageManager {
struct refInfo {
bytes20 hash;
uint96 index;
}
struct refData {
bytes20 hash;
bytes name;
}
mapping(bytes => address) public repoNameToOwner;
mapping(bytes => refInfo) public nameToRefInfo; // dev => {hash: 0x1234..., index: 1 }
bytes[] public refs; // [main, dev, test, staging]
function _convertRefInfo(
refInfo memory info
) internal view returns (refData memory res) {
res.hash = info.hash;
res.name = refs[info.index];
}
constructor() LargeStorageManager(0) {}
modifier onlyOwner(bytes memory repoName) {
require(repoNameToOwner[repoName] == msg.sender, "only owner");
_;
}
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 _createRepo(bytes memory repoName) internal {
if (repoNameToOwner[repoName] == address(0)) {
repoNameToOwner[repoName] = msg.sender;
} else {
require(repoNameToOwner[repoName] == msg.sender, "only owner");
}
}
function upload(
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable {
_createRepo(repoName);
_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 {
_createRepo(repoName);
_putChunkFromCalldata(
keccak256(bytes.concat(repoName, "/", path)),
chunkId,
data,
msg.value
);
}
function remove(
bytes memory repoName,
bytes memory path
) external onlyOwner(repoName) {
// The actually process of remove will remove all the chunks
_remove(keccak256(bytes.concat(repoName, "/", path)), 0);
}
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)));
}
function listRefs() public view returns (refData[] memory list) {
// todo: Differentiate all refs corresponding to a repo
list = new refData[](refs.length);
for (uint index = 0; index < refs.length; index++) {
list[index] = _convertRefInfo(nameToRefInfo[refs[index]]);
}
}
function setRef(
bytes memory repoName,
bytes memory name,
bytes20 refHash
) public {
bytes memory fullName = bytes.concat(repoName, "/", name);
// only execute `sload` once to reduce gas consumption
refInfo memory srs;
srs = nameToRefInfo[fullName];
uint256 refsLen = refs.length;
_createRepo(repoName);
if (srs.hash == bytes20(0)) {
// store refHash for the first time
require(
refsLen <= uint256(uint96(int96(-1))),
"Refs exceed valid length"
);
nameToRefInfo[fullName].hash = refHash;
nameToRefInfo[fullName].index = uint96(refsLen);
refs.push(fullName);
} else {
// only update refHash
nameToRefInfo[fullName].hash = refHash;
}
}
function delRef(
bytes memory repoName,
bytes memory name
) public onlyOwner(repoName) {
bytes memory fullName = bytes.concat(repoName, "/", name);
// only execute `sload` once to reduce gas consumption
refInfo memory srs;
srs = nameToRefInfo[fullName];
uint256 refsLen = refs.length;
require(
srs.hash != bytes20(0),
"Reference of this name does not exist"
);
require(srs.index < refsLen, "System Error: Invalid index");
if (srs.index < refsLen - 1) {
refs[srs.index] = refs[refsLen - 1];
nameToRefInfo[refs[refsLen - 1]].index = srs.index;
}
refs.pop();
delete nameToRefInfo[fullName];
}
}