From 7e9084de37c8cd44cb4116fa7c465bdc3e0b3b34 Mon Sep 17 00:00:00 2001 From: cyl19970726 <15258378443@163.com> Date: Sun, 19 Feb 2023 20:16:37 +0800 Subject: [PATCH] add git3-v3 --- contracts/v3/Hub.sol | 97 +++++++++++++++++++++ contracts/v3/HubFactory.sol | 12 +++ contracts/v3/Repository.sol | 111 +++++++++++++++++++++++++ contracts/v3/RepositoryAccess.sol | 28 +++++++ contracts/v3/storage/IStorageLayer.sol | 12 +++ 5 files changed, 260 insertions(+) create mode 100644 contracts/v3/Hub.sol create mode 100644 contracts/v3/HubFactory.sol create mode 100644 contracts/v3/Repository.sol create mode 100644 contracts/v3/RepositoryAccess.sol create mode 100644 contracts/v3/storage/IStorageLayer.sol diff --git a/contracts/v3/Hub.sol b/contracts/v3/Hub.sol new file mode 100644 index 0000000..d79b4f8 --- /dev/null +++ b/contracts/v3/Hub.sol @@ -0,0 +1,97 @@ +pragma solidity ^0.8.0; + +// import "hardhat/console.sol"; +// import "@openzeppelin/contracts/access/Ownable.sol"; + +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; +import "./Repository.sol"; +contract Hub is AccessControlEnumerableUpgradeable{ + // AccessController public accessController; + + mapping(bytes=>address) public nameToRepository; + bytes[] public repoNames; + + bytes32 public constant CREATOR = bytes32(uint256(1)); + bytes32 public constant MANAGER = bytes32(uint256(2)); + bytes32 public constant CONTRIBUTOR = bytes32(uint256(3)); + bytes32[] public RoleList = [CREATOR,MANAGER,CONTRIBUTOR]; + // mapping(bytes32=>address)public executors; + + bool public permissionless; + + function openPermissonlessJoin(bool open) public { + require(hasRole(CREATOR, _msgSender())); + permissionless = open; + } + + function memberShip() public view returns(bool){ + if (hasRole(CREATOR, _msgSender())){ + return true; + }else if (hasRole(MANAGER, _msgSender())){ + return true; + }else if (hasRole(CONTRIBUTOR, _msgSender())){ + return true; + }else{ + return false; + } + } + + //createRepository can be invoked by anyone within Hub + function createRepository(bytes memory repoName) public returns(address){ + require(hasRole(CREATOR, _msgSender())); + 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( + nameToRepository[repoName] == address(0), + "RepoName already exist" + ); + address repo = address(new Repository(repoName)); + nameToRepository[repoName] = repo; + 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]; + delete(nameToRepository[repoName]); + // todo:remove repoName from repoNames + return repoAddr; + } + + function addMember( bytes32 senderRole , bytes32 role,address member) public{ + require(hasRole(senderRole, _msgSender())); + require(senderRole>role); + grantRole(role, member); + } + + + function deleteMember(bytes32 senderRole ,bytes32 role,address member) public{ + require(hasRole(CREATOR, _msgSender())); + require(hasRole(senderRole, _msgSender())); + require(senderRole>role); + revokeRole(role, member); + } + + // permissionlessJoin can be invoked by everyone who want to join this hub + function permissionlessJoin() public{ + require(permissionless,"permissionless join no open"); + grantRole(CONTRIBUTOR, _msgSender()); + } + + +} diff --git a/contracts/v3/HubFactory.sol b/contracts/v3/HubFactory.sol new file mode 100644 index 0000000..536fdfd --- /dev/null +++ b/contracts/v3/HubFactory.sol @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000..4e7c0ab --- /dev/null +++ b/contracts/v3/Repository.sol @@ -0,0 +1,111 @@ +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 new file mode 100644 index 0000000..61945ca --- /dev/null +++ b/contracts/v3/RepositoryAccess.sol @@ -0,0 +1,28 @@ +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/storage/IStorageLayer.sol b/contracts/v3/storage/IStorageLayer.sol new file mode 100644 index 0000000..7ff417a --- /dev/null +++ b/contracts/v3/storage/IStorageLayer.sol @@ -0,0 +1,12 @@ +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