diff --git a/contracts/SLI/Git3Hub.sol b/contracts/SLI/Git3Hub.sol new file mode 100644 index 0000000..4d1e0a3 --- /dev/null +++ b/contracts/SLI/Git3Hub.sol @@ -0,0 +1,150 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +// import "hardhat/console.sol"; +import "./Git3HubStorage.sol"; + +contract Git3Hub_SLI is Git3HubStorage_SLI { + event RepoCreated(bytes repoName, address owner); + event RepoOwnerTransfer(bytes repoName, address oldOwner, address newOwner); + event PushRef(bytes repoName, bytes ref); + + constructor() Git3HubStorage_SLI() {} + + modifier onlyOwner(bytes memory repoName) { + require(repoNameToOwner[repoName] == msg.sender, "only owner"); + _; + } + + function createRepo(bytes memory repoName) external { + require( + repoName.length > 0 && repoName.length <= 100, + "RepoName length must be 1-100" + ); + for (uint i; i < repoName.length; i++) { + bytes1 char = repoName[i]; + require( + (char >= 0x61 && char <= 0x7A) || //a-z + (char >= 0x41 && char <= 0x5A) || //A-Z + (char >= 0x30 && char <= 0x39) || //0-9 + (char == 0x2D || char == 0x2E || char == 0x5F), //-._ + "RepoName must be alphanumeric or -._" + ); + } + + require( + repoNameToOwner[repoName] == address(0), + "RepoName already exist" + ); + repoNameToOwner[repoName] = msg.sender; + emit RepoCreated(repoName, msg.sender); + } + + function transferOwnership( + bytes memory repoName, + address newOwner + ) external onlyOwner(repoName) { + require(newOwner != address(0), "newOwner must not be zero address"); + repoNameToOwner[repoName] = newOwner; + emit RepoOwnerTransfer(repoName, msg.sender, newOwner); + } + + function download( + bytes memory repoName, + bytes memory path + ) external view returns (bytes memory) { + return pathToHash[keccak256(bytes.concat(repoName, "/", path))]; + } + + function upload( + bytes memory repoName, + bytes memory path, + bytes calldata data + ) external payable onlyOwner(repoName) { + pathToHash[keccak256(bytes.concat(repoName, "/", path))] = data; + } + + function remove( + bytes memory repoName, + bytes memory path + ) external onlyOwner(repoName) { + // The actually process of remove will remove all the chunks + pathToHash[keccak256(bytes.concat(repoName, "/", path))] = ""; + } + + function listRefs( + bytes memory repoName + ) external view returns (refData[] memory list) { + list = new refData[](repoNameToRefs[repoName].length); + for (uint index = 0; index < repoNameToRefs[repoName].length; index++) { + list[index] = _convertRefInfo( + repoName, + nameToRefInfo[repoNameToRefs[repoName][index]] + ); + } + } + + function setRef( + bytes memory repoName, + bytes memory ref, + bytes20 refHash + ) external onlyOwner(repoName) { + bytes memory fullName = bytes.concat(repoName, "/", ref); + // only execute `sload` once to reduce gas consumption + refInfo memory srs; + srs = nameToRefInfo[fullName]; + uint256 refsLen = repoNameToRefs[repoName].length; + + 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); + + repoNameToRefs[repoName].push(fullName); + } else { + // only update refHash + nameToRefInfo[fullName].hash = refHash; + } + emit PushRef(repoName, ref); + } + + function delRef( + bytes memory repoName, + bytes memory ref + ) external onlyOwner(repoName) { + bytes memory fullName = bytes.concat(repoName, "/", ref); + // only execute `sload` once to reduce gas consumption + refInfo memory srs; + srs = nameToRefInfo[fullName]; + uint256 refsLen = repoNameToRefs[repoName].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) { + repoNameToRefs[repoName][srs.index] = repoNameToRefs[repoName][ + refsLen - 1 + ]; + nameToRefInfo[repoNameToRefs[repoName][refsLen - 1]].index = srs + .index; + } + repoNameToRefs[repoName].pop(); + delete nameToRefInfo[fullName]; + } + + function _convertRefInfo( + bytes memory repoName, + refInfo memory info + ) internal view returns (refData memory res) { + res.hash = info.hash; + res.name = repoNameToRefs[repoName][info.index]; + } +} diff --git a/contracts/SLI/Git3HubStorage.sol b/contracts/SLI/Git3HubStorage.sol new file mode 100644 index 0000000..0733843 --- /dev/null +++ b/contracts/SLI/Git3HubStorage.sol @@ -0,0 +1,22 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +contract Git3HubStorage_SLI { + struct refInfo { + bytes20 hash; + uint96 index; + } + + struct refData { + bytes20 hash; + bytes name; + } + // FileStorage Storage Layout + mapping(bytes32 => bytes) public pathToHash; + + + // Git3Hub Storage Layout + mapping(bytes => address) public repoNameToOwner; + mapping(bytes => refInfo) public nameToRefInfo; // dev => {hash: 0x1234..., index: 1 } + mapping(bytes => bytes[]) public repoNameToRefs; // [main, dev, test, staging] +} diff --git a/contracts/SLI/testfvm.sol b/contracts/SLI/testfvm.sol new file mode 100644 index 0000000..e128d8f --- /dev/null +++ b/contracts/SLI/testfvm.sol @@ -0,0 +1,10 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +contract Hello{ + string public abc; + + constructor(){ + abc="Hello World"; + } +} \ No newline at end of file diff --git a/contracts/Git3Hub.sol b/contracts/ethstorage/Git3Hub.sol similarity index 99% rename from contracts/Git3Hub.sol rename to contracts/ethstorage/Git3Hub.sol index 10fc293..03e520a 100644 --- a/contracts/Git3Hub.sol +++ b/contracts/ethstorage/Git3Hub.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; // import "@openzeppelin/contracts/access/Ownable.sol"; import "./v2/LargeStorageManagerV2.sol"; -contract Git3Hub is LargeStorageManagerV2 { +contract Git3Hub_ES is LargeStorageManagerV2 { event RepoCreated(bytes repoName, address owner); event RepoOwnerTransfer(bytes repoName, address oldOwner, address newOwner); event PushRef(bytes repoName, bytes ref); diff --git a/contracts/Git3HubStorage.sol b/contracts/ethstorage/Git3HubStorage.sol similarity index 91% rename from contracts/Git3HubStorage.sol rename to contracts/ethstorage/Git3HubStorage.sol index 799fdb4..1e05ecb 100644 --- a/contracts/Git3HubStorage.sol +++ b/contracts/ethstorage/Git3HubStorage.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; -contract Git3HubStorage { +contract Git3HubStorage_ES { struct refInfo { bytes20 hash; uint96 index; @@ -14,10 +14,10 @@ contract Git3HubStorage { // LargeStorageManagerV2 Storage Layout mapping(bytes32 => mapping(uint256 => bytes32)) internal keyToMetadata; mapping(bytes32 => mapping(uint256 => mapping(uint256 => bytes32))) - internal keyToSlots; + internal keyToSlots; // Git3Hub Storage Layout mapping(bytes => address) public repoNameToOwner; mapping(bytes => refInfo) public nameToRefInfo; // dev => {hash: 0x1234..., index: 1 } mapping(bytes => bytes[]) public repoNameToRefs; // [main, dev, test, staging] -} \ No newline at end of file +} diff --git a/contracts/v2/LargeStorageManagerV2.sol b/contracts/ethstorage/v2/LargeStorageManagerV2.sol similarity index 96% rename from contracts/v2/LargeStorageManagerV2.sol rename to contracts/ethstorage/v2/LargeStorageManagerV2.sol index 34b7934..be53a62 100644 --- a/contracts/v2/LargeStorageManagerV2.sol +++ b/contracts/ethstorage/v2/LargeStorageManagerV2.sol @@ -7,11 +7,11 @@ import "./StorageSlotSelfDestructableV2.sol"; import "../Git3HubStorage.sol"; // Large storage manager to support arbitrarily-sized data with multiple chunk -contract LargeStorageManagerV2 is Git3HubStorage { +contract LargeStorageManagerV2 is Git3HubStorage_ES { using SlotHelper for bytes32; using SlotHelper for address; - uint8 internal constant SLOT_LIMIT = 0; + uint8 internal constant SLOT_LIMIT = 0; function isOptimize() public pure returns (bool) { return SLOT_LIMIT > 0; diff --git a/contracts/v2/LargerStorageManagerV2Test.sol b/contracts/ethstorage/v2/LargerStorageManagerV2Test.sol similarity index 100% rename from contracts/v2/LargerStorageManagerV2Test.sol rename to contracts/ethstorage/v2/LargerStorageManagerV2Test.sol diff --git a/contracts/v2/Memory.sol b/contracts/ethstorage/v2/Memory.sol similarity index 100% rename from contracts/v2/Memory.sol rename to contracts/ethstorage/v2/Memory.sol diff --git a/contracts/v2/StorageHelperV2.sol b/contracts/ethstorage/v2/StorageHelperV2.sol similarity index 100% rename from contracts/v2/StorageHelperV2.sol rename to contracts/ethstorage/v2/StorageHelperV2.sol diff --git a/contracts/v2/StorageSlotFactory.sol b/contracts/ethstorage/v2/StorageSlotFactory.sol similarity index 100% rename from contracts/v2/StorageSlotFactory.sol rename to contracts/ethstorage/v2/StorageSlotFactory.sol diff --git a/contracts/v2/StorageSlotSelfDestructableV2.sol b/contracts/ethstorage/v2/StorageSlotSelfDestructableV2.sol similarity index 100% rename from contracts/v2/StorageSlotSelfDestructableV2.sol rename to contracts/ethstorage/v2/StorageSlotSelfDestructableV2.sol diff --git a/contracts/v2/optimize/SlotHelper.sol b/contracts/ethstorage/v2/optimize/SlotHelper.sol similarity index 100% rename from contracts/v2/optimize/SlotHelper.sol rename to contracts/ethstorage/v2/optimize/SlotHelper.sol diff --git a/contracts/v2/optimize/SlotHelperTest.sol b/contracts/ethstorage/v2/optimize/SlotHelperTest.sol similarity index 100% rename from contracts/v2/optimize/SlotHelperTest.sol rename to contracts/ethstorage/v2/optimize/SlotHelperTest.sol diff --git a/scripts/create-repo.ts b/scripts/create-repo.ts index e20bc7e..9a38169 100644 --- a/scripts/create-repo.ts +++ b/scripts/create-repo.ts @@ -3,25 +3,40 @@ const { ethers } = hre; import fs from "fs"; async function main() { - const accounts = await ethers.getSigners(); - console.log(accounts[0].address); + const accounts = await ethers.getSigners(); + console.log(accounts[0].address); + let provider = ethers.provider; + let price = await provider.getFeeData(); - const Git3 = await hre.ethers.getContractAt( - "Git3Hub", - "0xee2879cd03A3D82C0Ffb648AA5773bcEBb0d5741" - ); - let rept - // let owner = await Git3.repoNameToOwner(Buffer.from("helloworld1")) - // console.log(owner) - // return - rept = await Git3.createRepo(Buffer.from("helloworld")) - console.log("rept", "https://explorer.galileo.web3q.io/tx/" + rept.hash); + const Git3 = await hre.ethers.getContractAt( + "Git3Hub_SLI", + "0xF56A1dd941667911896B9B872AC79E56cfc6a3dB" + ); + let rept; + let repoName = Buffer.from("h2"); + let owner = await Git3.repoNameToOwner(repoName); + console.log(owner); - // rept = await Git3.transferOwnership(Buffer.from("helloworld"), "0x1eD9c2F6814eA5225Bb78f2F2CA802Ded120077A") - // console.log("rept", "https://explorer.galileo.web3q.io/tx/" + rept.hash) + rept = await Git3.createRepo(repoName, { + type: 2, + maxFeePerGas: price.maxFeePerGas!, + maxPriorityFeePerGas: price.maxPriorityFeePerGas!, + }); + console.log("rept", "https://explorer.galileo.web3q.io/tx/" + rept.hash); + + rept = await Git3.transferOwnership( + repoName, + "0x1eD9c2F6814eA5225Bb78f2F2CA802Ded120077A", + { + type: 2, + maxFeePerGas: price.maxFeePerGas!, + maxPriorityFeePerGas: price.maxPriorityFeePerGas!, + } + ); + console.log("rept", "https://explorer.galileo.web3q.io/tx/" + rept.hash); } main().catch((error) => { - console.error(error); - process.exit(1); + console.error(error); + process.exit(1); }); diff --git a/scripts/deploy.ts b/scripts/deploy-ethstorage.ts similarity index 95% rename from scripts/deploy.ts rename to scripts/deploy-ethstorage.ts index 3752632..03a8c89 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy-ethstorage.ts @@ -11,7 +11,7 @@ async function main() { console.log(operator.address, nonce) - const Git3 = await ethers.getContractFactory("Git3Hub"); + const Git3 = await ethers.getContractFactory("Git3Hub_ES"); const git3 = await Git3.deploy({ nonce: nonce }); let logicReceipt = await git3.deployed() diff --git a/scripts/deploy-sli.ts b/scripts/deploy-sli.ts new file mode 100644 index 0000000..346a4d4 --- /dev/null +++ b/scripts/deploy-sli.ts @@ -0,0 +1,51 @@ +import { ethers } from "hardhat"; + +async function main() { + let provider = ethers.provider; + let [operator] = await ethers.getSigners(); + let nonce = await operator.getTransactionCount(); + console.log(operator.address, nonce, await operator.getBalance()); + + let price = await provider.getFeeData(); + let net = await provider.getNetwork(); + console.log(price, net.chainId); + + const Git3 = await ethers.getContractFactory("Git3Hub_SLI"); + const git3 = await Git3.deploy({ + nonce: nonce, + type: 2, + maxFeePerGas: price.maxFeePerGas!, + maxPriorityFeePerGas: price.maxPriorityFeePerGas!, + gasLimit: 3000000, + }); + + let logicReceipt = await git3.deployed(); + console.log(logicReceipt.deployTransaction.hash); + nonce++; + + let factory1 = await ethers.getContractFactory("UpgradeableProxy"); + // Proxy don't need to init Git3 contract because the constructor is empty. + let initSelector = "0x"; + + let proxyInstance = await factory1 + .connect(operator) + .deploy(git3.address, operator.address, initSelector, { + nonce: nonce, + type: 2, + maxFeePerGas: price.maxFeePerGas, + maxPriorityFeePerGas: price.maxPriorityFeePerGas, + }); + let proxyReceipt = await proxyInstance.deployed(); + console.log(proxyReceipt.deployTransaction.hash); + + // console.log({logicReceipt,proxyReceipt}); + console.log("Logic Contract", git3.address); + console.log("Proxy Contract", proxyInstance.address); +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/download-file.ts b/scripts/download-file.ts new file mode 100644 index 0000000..73e9d38 --- /dev/null +++ b/scripts/download-file.ts @@ -0,0 +1,24 @@ +import hre from "hardhat"; +const { ethers } = hre; +import fs from "fs"; + +async function main() { + const accounts = await ethers.getSigners(); + console.log(accounts[0].address); + + const Git3 = await hre.ethers.getContractAt( + "Git3Hub_SLI", + "0xF56A1dd941667911896B9B872AC79E56cfc6a3dB" + ); + + let res = await Git3.download( + Buffer.from("h1"), + Buffer.from("objects/9f/2781f252bddce27d26a4e9ae4acf965f09ba9f") + ); + console.log(res); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +});